+/*
+asm-placeholder.exe is used below to disassemble objc_msgSend
+
+TEST_BUILD
+ $C{COMPILE} -x assembler $DIR/asm-placeholder.s -o asm-placeholder.exe
+ $C{COMPILE} $DIR/msgSend.m -o msgSend.exe -Wno-unused-parameter -Wundeclared-selector -D__DARWIN_OPAQUE_ARM_THREAD_STATE64=1
+END
+*/
+
+#include "test.h"
+#include "testroot.i"
+
+#include <sys/stat.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <objc/objc-internal.h>
+#include <objc/objc-abi.h>
+#include <simd/simd.h>
+#include <mach-o/loader.h>
+
+// rdar://21694990 simd.h should have a vector_equal(a, b) function
+static bool vector_equal(vector_ulong2 lhs, vector_ulong2 rhs) {
+ return vector_all(lhs == rhs);
+}
+
+#if __arm64__
+ // no stret dispatchers
+# define SUPPORT_STRET 0
+# define objc_msgSend_stret objc_msgSend
+# define objc_msgSendSuper2_stret objc_msgSendSuper2
+# define objc_msgSend_stret_debug objc_msgSend_debug
+# define objc_msgSendSuper2_stret_debug objc_msgSendSuper2_debug
+# define objc_msgLookup_stret objc_msgLookup
+# define objc_msgLookupSuper2_stret objc_msgLookupSuper2
+# define method_invoke_stret method_invoke
+#else
+# define SUPPORT_STRET 1
+#endif
+
+
+#if defined(__arm__)
+// rdar://8331406
+# define ALIGN_()
+#else
+# define ALIGN_() asm(".align 4");
+#endif
+
+@interface Super : TestRoot @end
+
+@interface Sub : Super @end
+
+static int state = 0;
+
+static id SELF;
+
+// for typeof() shorthand only
+id (*idmsg0)(id, SEL) __attribute__((unused));
+long long (*llmsg0)(id, SEL) __attribute__((unused));
+// struct stret (*stretmsg0)(id, SEL) __attribute__((unused));
+double (*fpmsg0)(id, SEL) __attribute__((unused));
+long double (*lfpmsg0)(id, SEL) __attribute__((unused));
+vector_ulong2 (*vecmsg0)(id, SEL) __attribute__((unused));
+
+#define VEC1 ((vector_ulong2){1, 1})
+#define VEC2 ((vector_ulong2){2, 2})
+#define VEC3 ((vector_ulong2){3, 3})
+#define VEC4 ((vector_ulong2){4, 4})
+#define VEC5 ((vector_ulong2){5, 5})
+#define VEC6 ((vector_ulong2){6, 6})
+#define VEC7 ((vector_ulong2){7, 7})
+#define VEC8 ((vector_ulong2){8, 8})
+
+#define CHECK_ARGS(sel) \
+do { \
+ testassert(self == SELF); \
+ testassert(_cmd == sel_registerName(#sel "::::::::::::::::::::::::::::::::::::"));\
+ testassert(i1 == 1); \
+ testassert(i2 == 2); \
+ testassert(i3 == 3); \
+ testassert(i4 == 4); \
+ testassert(i5 == 5); \
+ testassert(i6 == 6); \
+ testassert(i7 == 7); \
+ testassert(i8 == 8); \
+ testassert(i9 == 9); \
+ testassert(i10 == 10); \
+ testassert(i11 == 11); \
+ testassert(i12 == 12); \
+ testassert(i13 == 13); \
+ testassert(f1 == 1.0); \
+ testassert(f2 == 2.0); \
+ testassert(f3 == 3.0); \
+ testassert(f4 == 4.0); \
+ testassert(f5 == 5.0); \
+ testassert(f6 == 6.0); \
+ testassert(f7 == 7.0); \
+ testassert(f8 == 8.0); \
+ testassert(f9 == 9.0); \
+ testassert(f10 == 10.0); \
+ testassert(f11 == 11.0); \
+ testassert(f12 == 12.0); \
+ testassert(f13 == 13.0); \
+ testassert(f14 == 14.0); \
+ testassert(f15 == 15.0); \
+ testassert(vector_all(v1 == 1)); \
+ testassert(vector_all(v2 == 2)); \
+ testassert(vector_all(v3 == 3)); \
+ testassert(vector_all(v4 == 4)); \
+ testassert(vector_all(v5 == 5)); \
+ testassert(vector_all(v6 == 6)); \
+ testassert(vector_all(v7 == 7)); \
+ testassert(vector_all(v8 == 8)); \
+} while (0)
+
+#define CHECK_ARGS_NOARG(sel) \
+do { \
+ testassert(self == SELF); \
+ testassert(_cmd == sel_registerName(#sel "_noarg"));\
+} while (0)
+
+id NIL_RECEIVER;
+id ID_RESULT;
+long long LL_RESULT = __LONG_LONG_MAX__ - 2LL*__INT_MAX__;
+double FP_RESULT = __DBL_MIN__ + __DBL_EPSILON__;
+long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
+vector_ulong2 VEC_RESULT = { 0x1234567890abcdefULL, 0xfedcba0987654321ULL };
+// STRET_RESULT in test.h
+
+static struct stret zero;
+
+struct stret_i1 {
+ uintptr_t i1;
+};
+struct stret_i2 {
+ uintptr_t i1;
+ uintptr_t i2;
+};
+struct stret_i3 {
+ uintptr_t i1;
+ uintptr_t i2;
+ uintptr_t i3;
+};
+struct stret_i4 {
+ uintptr_t i1;
+ uintptr_t i2;
+ uintptr_t i3;
+};
+struct stret_i5 {
+ uintptr_t i1;
+ uintptr_t i2;
+ uintptr_t i3;
+ uintptr_t i4;
+ uintptr_t i5;
+};
+struct stret_i6 {
+ uintptr_t i1;
+ uintptr_t i2;
+ uintptr_t i3;
+ uintptr_t i4;
+ uintptr_t i5;
+ uintptr_t i6;
+};
+struct stret_i7 {
+ uintptr_t i1;
+ uintptr_t i2;
+ uintptr_t i3;
+ uintptr_t i4;
+ uintptr_t i5;
+ uintptr_t i6;
+ uintptr_t i7;
+};
+struct stret_i8 {
+ uintptr_t i1;
+ uintptr_t i2;
+ uintptr_t i3;
+ uintptr_t i4;
+ uintptr_t i5;
+ uintptr_t i8;
+ uintptr_t i9;
+};
+struct stret_i9 {
+ uintptr_t i1;
+ uintptr_t i2;
+ uintptr_t i3;
+ uintptr_t i4;
+ uintptr_t i5;
+ uintptr_t i6;
+ uintptr_t i7;
+ uintptr_t i8;
+ uintptr_t i9;
+};
+
+struct stret_d1 {
+ double d1;
+};
+struct stret_d2 {
+ double d1;
+ double d2;
+};
+struct stret_d3 {
+ double d1;
+ double d2;
+ double d3;
+};
+struct stret_d4 {
+ double d1;
+ double d2;
+ double d3;
+};
+struct stret_d5 {
+ double d1;
+ double d2;
+ double d3;
+ double d4;
+ double d5;
+};
+struct stret_d6 {
+ double d1;
+ double d2;
+ double d3;
+ double d4;
+ double d5;
+ double d6;
+};
+struct stret_d7 {
+ double d1;
+ double d2;
+ double d3;
+ double d4;
+ double d5;
+ double d6;
+ double d7;
+};
+struct stret_d8 {
+ double d1;
+ double d2;
+ double d3;
+ double d4;
+ double d5;
+ double d8;
+ double d9;
+};
+struct stret_d9 {
+ double d1;
+ double d2;
+ double d3;
+ double d4;
+ double d5;
+ double d6;
+ double d7;
+ double d8;
+ double d9;
+};
+
+
+@interface Super (Prototypes)
+
+// Method prototypes to pacify -Wundeclared-selector.
+
+-(id)idret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
+
+-(long long)llret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
+
+-(struct stret)stret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
+
+-(double)fpret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
+
+-(long double)lfpret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
+
+-(vector_ulong2)vecret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
+
+@end
+
+
+// Zero all volatile registers.
+#if __cplusplus
+extern "C"
+#endif
+void stomp(void);
+
+#if __x86_64__
+asm("\n .text"
+ "\n .globl _stomp"
+ "\n _stomp:"
+ "\n mov $0, %rax"
+ "\n mov $0, %rcx"
+ "\n mov $0, %rdx"
+ "\n mov $0, %rsi"
+ "\n mov $0, %rdi"
+ "\n mov $0, %r8"
+ "\n mov $0, %r9"
+ "\n mov $0, %r10"
+ "\n mov $0, %r11"
+ "\n xorps %xmm0, %xmm0"
+ "\n xorps %xmm1, %xmm1"
+ "\n xorps %xmm2, %xmm2"
+ "\n xorps %xmm3, %xmm3"
+ "\n xorps %xmm4, %xmm4"
+ "\n xorps %xmm5, %xmm5"
+ "\n xorps %xmm6, %xmm6"
+ "\n xorps %xmm7, %xmm7"
+ "\n xorps %xmm8, %xmm8"
+ "\n xorps %xmm9, %xmm9"
+ "\n xorps %xmm10, %xmm10"
+ "\n xorps %xmm11, %xmm11"
+ "\n xorps %xmm12, %xmm12"
+ "\n xorps %xmm13, %xmm13"
+ "\n xorps %xmm14, %xmm14"
+ "\n xorps %xmm15, %xmm15"
+ "\n ret");
+
+#elif __i386__
+asm("\n .text"
+ "\n .globl _stomp"
+ "\n _stomp:"
+ "\n mov $0, %eax"
+ "\n mov $0, %ecx"
+ "\n mov $0, %edx"
+ "\n xorps %xmm0, %xmm0"
+ "\n xorps %xmm1, %xmm1"
+ "\n xorps %xmm2, %xmm2"
+ "\n xorps %xmm3, %xmm3"
+ "\n xorps %xmm4, %xmm4"
+ "\n xorps %xmm5, %xmm5"
+ "\n xorps %xmm6, %xmm6"
+ "\n xorps %xmm7, %xmm7"
+ "\n ret");
+
+#elif __arm64__
+asm("\n .text"
+ "\n .globl _stomp"
+ "\n _stomp:"
+ "\n mov x0, #0"
+ "\n mov x1, #0"
+ "\n mov x2, #0"
+ "\n mov x3, #0"
+ "\n mov x4, #0"
+ "\n mov x5, #0"
+ "\n mov x6, #0"
+ "\n mov x7, #0"
+ "\n mov x8, #0"
+ "\n mov x9, #0"
+ "\n mov x10, #0"
+ "\n mov x11, #0"
+ "\n mov x12, #0"
+ "\n mov x13, #0"
+ "\n mov x14, #0"
+ "\n mov x15, #0"
+ "\n mov x16, #0"
+ "\n mov x17, #0"
+ "\n movi d0, #0"
+ "\n movi d1, #0"
+ "\n movi d2, #0"
+ "\n movi d3, #0"
+ "\n movi d4, #0"
+ "\n movi d5, #0"
+ "\n movi d6, #0"
+ "\n movi d7, #0"
+ "\n ret"
+ );
+
+#elif __arm__
+asm("\n .text"
+ "\n .globl _stomp"
+ "\n .thumb_func _stomp"
+ "\n _stomp:"
+ "\n mov r0, #0"
+ "\n mov r1, #0"
+ "\n mov r2, #0"
+ "\n mov r3, #0"
+ "\n mov r9, #0"
+ "\n mov r12, #0"
+ "\n vmov.i32 q0, #0"
+ "\n vmov.i32 q1, #0"
+ "\n vmov.i32 q2, #0"
+ "\n vmov.i32 q3, #0"
+ "\n vmov.i32 q8, #0"
+ "\n vmov.i32 q9, #0"
+ "\n vmov.i32 q10, #0"
+ "\n vmov.i32 q11, #0"
+ "\n vmov.i32 q12, #0"
+ "\n vmov.i32 q13, #0"
+ "\n vmov.i32 q14, #0"
+ "\n vmov.i32 q15, #0"
+ "\n bx lr"
+ );
+
+#else
+# error unknown architecture
+#endif
+
+
+@implementation Super
+-(struct stret)stret { return STRET_RESULT; }
+
+// The IMPL_ methods are not called directly. Instead the non IMPL_ name is
+// called. The resolver function installs the real method. This allows
+// the resolver function to stomp on registers to help test register
+// preservation in the uncached path.
+
++(BOOL) resolveInstanceMethod:(SEL)sel
+{
+ const char *name = sel_getName(sel);
+ if (! strstr(name, "::::::::")) return false;
+
+ testprintf("resolving %s\n", name);
+
+ stomp();
+ char *realName;
+ asprintf(&realName, "IMPL_%s", name);
+ SEL realSel = sel_registerName(realName);
+ free(realName);
+
+ IMP imp = class_getMethodImplementation(self, realSel);
+ if (imp == &_objc_msgForward) return false;
+ return class_addMethod(self, sel, imp, "");
+}
+
+-(id)IMPL_idret:
+(vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ CHECK_ARGS(idret);
+ state = 1;
+ return ID_RESULT;
+}
+
+-(long long)IMPL_llret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ CHECK_ARGS(llret);
+ state = 2;
+ return LL_RESULT;
+}
+
+-(struct stret)IMPL_stret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ CHECK_ARGS(stret);
+ state = 3;
+ return STRET_RESULT;
+}
+
+-(double)IMPL_fpret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ CHECK_ARGS(fpret);
+ state = 4;
+ return FP_RESULT;
+}
+
+-(long double)IMPL_lfpret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ CHECK_ARGS(lfpret);
+ state = 5;
+ return LFP_RESULT;
+}
+
+-(vector_ulong2)IMPL_vecret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ CHECK_ARGS(vecret);
+ state = 6;
+ return VEC_RESULT;
+}
+
+
+-(id)idret_noarg
+{
+ CHECK_ARGS_NOARG(idret);
+ state = 11;
+ return ID_RESULT;
+}
+
+-(long long)llret_noarg
+{
+ CHECK_ARGS_NOARG(llret);
+ state = 12;
+ return LL_RESULT;
+}
+
+-(struct stret)stret_noarg
+{
+ CHECK_ARGS_NOARG(stret);
+ state = 13;
+ return STRET_RESULT;
+}
+
+-(double)fpret_noarg
+{
+ CHECK_ARGS_NOARG(fpret);
+ state = 14;
+ return FP_RESULT;
+}
+
+-(long double)lfpret_noarg
+{
+ CHECK_ARGS_NOARG(lfpret);
+ state = 15;
+ return LFP_RESULT;
+}
+
+-(vector_ulong2)vecret_noarg
+{
+ CHECK_ARGS_NOARG(vecret);
+ state = 16;
+ return VEC_RESULT;
+}
+
+
+-(struct stret)stret_nop
+{
+ return STRET_RESULT;
+}
+
+
+#define STRET_IMP(n) \
++(struct stret_##n)stret_##n##_zero \
+{ \
+ struct stret_##n ret; \
+ bzero(&ret, sizeof(ret)); \
+ return ret; \
+} \
++(struct stret_##n)stret_##n##_nonzero \
+{ \
+ struct stret_##n ret; \
+ memset(&ret, 0xff, sizeof(ret)); \
+ return ret; \
+}
+
+STRET_IMP(i1)
+STRET_IMP(i2)
+STRET_IMP(i3)
+STRET_IMP(i4)
+STRET_IMP(i5)
+STRET_IMP(i6)
+STRET_IMP(i7)
+STRET_IMP(i8)
+STRET_IMP(i9)
+
+STRET_IMP(d1)
+STRET_IMP(d2)
+STRET_IMP(d3)
+STRET_IMP(d4)
+STRET_IMP(d5)
+STRET_IMP(d6)
+STRET_IMP(d7)
+STRET_IMP(d8)
+STRET_IMP(d9)
+
+
++(id)idret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ fail("+idret called instead of -idret");
+ CHECK_ARGS(idret);
+}
+
++(long long)llret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ fail("+llret called instead of -llret");
+ CHECK_ARGS(llret);
+}
+
++(struct stret)stret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ fail("+stret called instead of -stret");
+ CHECK_ARGS(stret);
+}
+
++(double)fpret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ fail("+fpret called instead of -fpret");
+ CHECK_ARGS(fpret);
+}
+
++(long double)lfpret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ fail("+lfpret called instead of -lfpret");
+ CHECK_ARGS(lfpret);
+}
+
++(id)idret_noarg
+{
+ fail("+idret_noarg called instead of -idret_noarg");
+ CHECK_ARGS_NOARG(idret);
+}
+
++(long long)llret_noarg
+{
+ fail("+llret_noarg called instead of -llret_noarg");
+ CHECK_ARGS_NOARG(llret);
+}
+
++(struct stret)stret_noarg
+{
+ fail("+stret_noarg called instead of -stret_noarg");
+ CHECK_ARGS_NOARG(stret);
+}
+
++(double)fpret_noarg
+{
+ fail("+fpret_noarg called instead of -fpret_noarg");
+ CHECK_ARGS_NOARG(fpret);
+}
+
++(long double)lfpret_noarg
+{
+ fail("+lfpret_noarg called instead of -lfpret_noarg");
+ CHECK_ARGS_NOARG(lfpret);
+}
+
++(vector_ulong2)vecret_noarg
+{
+ fail("+vecret_noarg called instead of -vecret_noarg");
+ CHECK_ARGS_NOARG(vecret);
+}
+
+@end
+
+
+@implementation Sub
+
+-(id)IMPL_idret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ id result;
+ CHECK_ARGS(idret);
+ state = 100;
+ result = [super idret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
+ testassert(state == 1);
+ testassert(result == ID_RESULT);
+ state = 101;
+ return result;
+}
+
+-(long long)IMPL_llret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ long long result;
+ CHECK_ARGS(llret);
+ state = 100;
+ result = [super llret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
+ testassert(state == 2);
+ testassert(result == LL_RESULT);
+ state = 102;
+ return result;
+}
+
+-(struct stret)IMPL_stret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ struct stret result;
+ CHECK_ARGS(stret);
+ state = 100;
+ result = [super stret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
+ testassert(state == 3);
+ testassert(stret_equal(result, STRET_RESULT));
+ state = 103;
+ return result;
+}
+
+-(double)IMPL_fpret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ double result;
+ CHECK_ARGS(fpret);
+ state = 100;
+ result = [super fpret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
+ testassert(state == 4);
+ testassert(result == FP_RESULT);
+ state = 104;
+ return result;
+}
+
+-(long double)IMPL_lfpret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ long double result;
+ CHECK_ARGS(lfpret);
+ state = 100;
+ result = [super lfpret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
+ testassert(state == 5);
+ testassert(result == LFP_RESULT);
+ state = 105;
+ return result;
+}
+
+-(vector_ulong2)IMPL_vecret:
+ (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
+{
+ vector_ulong2 result;
+ CHECK_ARGS(vecret);
+ state = 100;
+ result = [super vecret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
+ testassert(state == 6);
+ testassert(vector_equal(result, VEC_RESULT));
+ state = 106;
+ return result;
+}
+
+
+-(id)idret_noarg
+{
+ id result;
+ CHECK_ARGS_NOARG(idret);
+ state = 100;
+ result = [super idret_noarg];
+ testassert(state == 11);
+ testassert(result == ID_RESULT);
+ state = 111;
+ return result;
+}
+
+-(long long)llret_noarg
+{
+ long long result;
+ CHECK_ARGS_NOARG(llret);
+ state = 100;
+ result = [super llret_noarg];
+ testassert(state == 12);
+ testassert(result == LL_RESULT);
+ state = 112;
+ return result;
+}
+
+-(struct stret)stret_noarg
+{
+ struct stret result;
+ CHECK_ARGS_NOARG(stret);
+ state = 100;
+ result = [super stret_noarg];
+ testassert(state == 13);
+ testassert(stret_equal(result, STRET_RESULT));
+ state = 113;
+ return result;
+}
+
+-(double)fpret_noarg
+{
+ double result;
+ CHECK_ARGS_NOARG(fpret);
+ state = 100;
+ result = [super fpret_noarg];
+ testassert(state == 14);
+ testassert(result == FP_RESULT);
+ state = 114;
+ return result;
+}
+
+-(long double)lfpret_noarg
+{
+ long double result;
+ CHECK_ARGS_NOARG(lfpret);
+ state = 100;
+ result = [super lfpret_noarg];
+ testassert(state == 15);
+ testassert(result == LFP_RESULT);
+ state = 115;
+ return result;
+}
+
+-(vector_ulong2)vecret_noarg
+{
+ vector_ulong2 result;
+ CHECK_ARGS_NOARG(vecret);
+ state = 100;
+ result = [super vecret_noarg];
+ testassert(state == 16);
+ testassert(vector_equal(result, VEC_RESULT));
+ state = 116;
+ return result;
+}
+
+@end
+
+
+#if OBJC_HAVE_TAGGED_POINTERS
+
+@interface TaggedSub : Sub @end
+
+@implementation TaggedSub : Sub
+
++(void)initialize
+{
+ _objc_registerTaggedPointerClass(OBJC_TAG_1, self);
+}
+
+@end
+
+@interface ExtTaggedSub : Sub @end
+
+@implementation ExtTaggedSub : Sub
+
++(void)initialize
+{
+ _objc_registerTaggedPointerClass(OBJC_TAG_First52BitPayload, self);
+}
+
+@end
+
+#endif
+
+
+// DWARF checking machinery
+
+#if TARGET_OS_WIN32
+// unimplemented on this platform
+#define NO_DWARF_REASON "(windows)"
+
+#elif TARGET_OS_WATCH
+// fixme unimplemented - ucontext not passed to signal handlers
+#define NO_DWARF_REASON "(watchOS)"
+
+#elif __has_feature(objc_arc)
+// ARC's extra RR calls hit the traps at the wrong times
+#define NO_DWARF_REASON "(ARC)"
+
+#else
+
+#define TEST_DWARF 1
+
+// Classes with no implementations and no cache contents from elsewhere.
+@interface SuperDW : TestRoot @end
+@implementation SuperDW @end
+
+@interface Sub0DW : SuperDW @end
+@implementation Sub0DW @end
+
+@interface SubDW : Sub0DW @end
+@implementation SubDW @end
+
+#include <dlfcn.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <libunwind.h>
+
+bool caught = false;
+uintptr_t clobbered;
+
+__BEGIN_DECLS
+extern void callit(void *obj, void *sel, void *fn);
+extern struct stret callit_stret(void *obj, void *sel, void *fn);
+__END_DECLS
+
+#if __x86_64__
+
+typedef uint8_t insn_t;
+typedef insn_t clobbered_insn_t;
+#define BREAK_INSN ((insn_t)0x06) // undefined
+#define BREAK_SIGNAL SIGILL
+
+uintptr_t r12 = 0;
+uintptr_t r13 = 0;
+uintptr_t r14 = 0;
+uintptr_t r15 = 0;
+uintptr_t rbx = 0;
+uintptr_t rbp = 0;
+uintptr_t rsp = 0;
+uintptr_t rip = 0;
+
+void handle_exception(x86_thread_state64_t *state)
+{
+ unw_cursor_t curs;
+ unw_word_t reg;
+ int err;
+ int step;
+
+ err = unw_init_local(&curs, (unw_context_t *)state);
+ testassert(!err);
+
+ step = unw_step(&curs);
+ testassert(step > 0);
+
+ err = unw_get_reg(&curs, UNW_X86_64_R12, ®);
+ testassert(!err);
+ testassert(reg == r12);
+
+ err = unw_get_reg(&curs, UNW_X86_64_R13, ®);
+ testassert(!err);
+ testassert(reg == r13);
+
+ err = unw_get_reg(&curs, UNW_X86_64_R14, ®);
+ testassert(!err);
+ testassert(reg == r14);
+
+ err = unw_get_reg(&curs, UNW_X86_64_R15, ®);
+ testassert(!err);
+ testassert(reg == r15);
+
+ err = unw_get_reg(&curs, UNW_X86_64_RBX, ®);
+ testassert(!err);
+ testassert(reg == rbx);
+
+ err = unw_get_reg(&curs, UNW_X86_64_RBP, ®);
+ testassert(!err);
+ testassert(reg == rbp);
+
+ err = unw_get_reg(&curs, UNW_X86_64_RSP, ®);
+ testassert(!err);
+ testassert(reg == rsp);
+
+ err = unw_get_reg(&curs, UNW_REG_IP, ®);
+ testassert(!err);
+ testassert(reg == rip);
+
+
+ // set thread state to unwound state
+ state->__r12 = r12;
+ state->__r13 = r13;
+ state->__r14 = r14;
+ state->__r15 = r15;
+ state->__rbx = rbx;
+ state->__rbp = rbp;
+ state->__rsp = rsp;
+ state->__rip = rip;
+
+ caught = true;
+}
+
+
+void break_handler(int sig, siginfo_t *info, void *cc)
+{
+ ucontext_t *uc = (ucontext_t *)cc;
+ mcontext_t mc = (mcontext_t)uc->uc_mcontext;
+
+ testprintf(" handled\n");
+
+ testassert(sig == BREAK_SIGNAL);
+ testassert((uintptr_t)info->si_addr == clobbered);
+
+ handle_exception(&mc->__ss);
+ // handle_exception changed register state for continuation
+}
+
+__asm__(
+"\n .text"
+"\n .globl _callit"
+"\n _callit:"
+// save sp and return address to variables
+"\n movq (%rsp), %r10"
+"\n movq %r10, _rip(%rip)"
+"\n movq %rsp, _rsp(%rip)"
+"\n addq $8, _rsp(%rip)" // rewind to pre-call value
+// save other non-volatile registers to variables
+"\n movq %rbx, _rbx(%rip)"
+"\n movq %rbp, _rbp(%rip)"
+"\n movq %r12, _r12(%rip)"
+"\n movq %r13, _r13(%rip)"
+"\n movq %r14, _r14(%rip)"
+"\n movq %r15, _r15(%rip)"
+"\n jmpq *%rdx"
+ );
+
+__asm__(
+"\n .text"
+"\n .globl _callit_stret"
+"\n _callit_stret:"
+// save sp and return address to variables
+"\n movq (%rsp), %r10"
+"\n movq %r10, _rip(%rip)"
+"\n movq %rsp, _rsp(%rip)"
+"\n addq $8, _rsp(%rip)" // rewind to pre-call value
+// save other non-volatile registers to variables
+"\n movq %rbx, _rbx(%rip)"
+"\n movq %rbp, _rbp(%rip)"
+"\n movq %r12, _r12(%rip)"
+"\n movq %r13, _r13(%rip)"
+"\n movq %r14, _r14(%rip)"
+"\n movq %r15, _r15(%rip)"
+"\n jmpq *%rcx"
+ );
+
+
+// x86_64
+
+#elif __i386__
+
+typedef uint8_t insn_t;
+typedef insn_t clobbered_insn_t;
+#define BREAK_INSN ((insn_t)0xcc) // int3
+#define BREAK_SIGNAL SIGTRAP
+
+uintptr_t eip = 0;
+uintptr_t esp = 0;
+uintptr_t ebx = 0;
+uintptr_t ebp = 0;
+uintptr_t edi = 0;
+uintptr_t esi = 0;
+uintptr_t espfix = 0;
+
+void handle_exception(i386_thread_state_t *state)
+{
+ unw_cursor_t curs;
+ unw_word_t reg;
+ int err;
+ int step;
+
+ err = unw_init_local(&curs, (unw_context_t *)state);
+ testassert(!err);
+
+ step = unw_step(&curs);
+ testassert(step > 0);
+
+ err = unw_get_reg(&curs, UNW_REG_IP, ®);
+ testassert(!err);
+ testassert(reg == eip);
+
+ err = unw_get_reg(&curs, UNW_X86_ESP, ®);
+ testassert(!err);
+ testassert(reg == esp);
+
+ err = unw_get_reg(&curs, UNW_X86_EBX, ®);
+ testassert(!err);
+ testassert(reg == ebx);
+
+ err = unw_get_reg(&curs, UNW_X86_EBP, ®);
+ testassert(!err);
+ testassert(reg == ebp);
+
+ err = unw_get_reg(&curs, UNW_X86_EDI, ®);
+ testassert(!err);
+ testassert(reg == edi);
+
+ err = unw_get_reg(&curs, UNW_X86_ESI, ®);
+ testassert(!err);
+ testassert(reg == esi);
+
+
+ // set thread state to unwound state
+ state->__eip = eip;
+ state->__esp = esp + espfix;
+ state->__ebx = ebx;
+ state->__ebp = ebp;
+ state->__edi = edi;
+ state->__esi = esi;
+
+ caught = true;
+}
+
+
+void break_handler(int sig, siginfo_t *info, void *cc)
+{
+ ucontext_t *uc = (ucontext_t *)cc;
+ mcontext_t mc = (mcontext_t)uc->uc_mcontext;
+
+ testprintf(" handled\n");
+
+ testassert(sig == BREAK_SIGNAL);
+ testassert((uintptr_t)info->si_addr-1 == clobbered);
+
+ handle_exception(&mc->__ss);
+ // handle_exception changed register state for continuation
+}
+
+__asm__(
+"\n .text"
+"\n .globl _callit"
+"\n _callit:"
+// save sp and return address to variables
+"\n call 1f"
+"\n 1: popl %edx"
+"\n movl (%esp), %eax"
+"\n movl %eax, _eip-1b(%edx)"
+"\n movl %esp, _esp-1b(%edx)"
+"\n addl $4, _esp-1b(%edx)" // rewind to pre-call value
+"\n movl $0, _espfix-1b(%edx)"
+// save other non-volatile registers to variables
+"\n movl %ebx, _ebx-1b(%edx)"
+"\n movl %ebp, _ebp-1b(%edx)"
+"\n movl %edi, _edi-1b(%edx)"
+"\n movl %esi, _esi-1b(%edx)"
+"\n jmpl *12(%esp)"
+ );
+
+__asm__(
+"\n .text"
+"\n .globl _callit_stret"
+"\n _callit_stret:"
+// save sp and return address to variables
+"\n call 1f"
+"\n 1: popl %edx"
+"\n movl (%esp), %eax"
+"\n movl %eax, _eip-1b(%edx)"
+"\n movl %esp, _esp-1b(%edx)"
+"\n addl $4, _esp-1b(%edx)" // rewind to pre-call value
+"\n movl $4, _espfix-1b(%edx)"
+// save other non-volatile registers to variables
+"\n movl %ebx, _ebx-1b(%edx)"
+"\n movl %ebp, _ebp-1b(%edx)"
+"\n movl %edi, _edi-1b(%edx)"
+"\n movl %esi, _esi-1b(%edx)"
+"\n jmpl *16(%esp)"
+ );
+
+
+// i386
+#elif __arm64__
+
+#include <sys/ucontext.h>
+
+typedef uint32_t insn_t;
+typedef insn_t clobbered_insn_t;
+#define BREAK_INSN ((insn_t)0xd4200020) // brk #1
+#define BREAK_SIGNAL SIGTRAP
+
+uintptr_t x19 = 0;
+uintptr_t x20 = 0;
+uintptr_t x21 = 0;
+uintptr_t x22 = 0;
+uintptr_t x23 = 0;
+uintptr_t x24 = 0;
+uintptr_t x25 = 0;
+uintptr_t x26 = 0;
+uintptr_t x27 = 0;
+uintptr_t x28 = 0;
+uintptr_t fp = 0;
+uintptr_t sp = 0;
+uintptr_t pc = 0;
+
+void handle_exception(arm_thread_state64_t *state)
+{
+ unw_cursor_t curs;
+ unw_word_t reg;
+ int err;
+ int step;
+
+ // libunwind layout differs from mcontext layout
+ // GPRs are the same but vector registers are not
+ unw_context_t unwstate;
+ unw_getcontext(&unwstate);
+ memcpy(&unwstate, state, sizeof(*state));
+
+ // libunwind and xnu sign some pointers differently
+ // xnu: not signed (fixme this may change?)
+ // libunwind: PC and LR both signed with return address key and SP
+ void **pcp = &((arm_thread_state64_t *)&unwstate)->__opaque_pc;
+ *pcp = ptrauth_sign_unauthenticated((void*)__darwin_arm_thread_state64_get_pc(*state),
+ ptrauth_key_return_address,
+ (ptrauth_extra_data_t)__darwin_arm_thread_state64_get_sp(*state));
+ void **lrp = &((arm_thread_state64_t *)&unwstate)->__opaque_lr;
+ *lrp = ptrauth_sign_unauthenticated((void*)__darwin_arm_thread_state64_get_lr(*state),
+ ptrauth_key_return_address,
+ (ptrauth_extra_data_t)__darwin_arm_thread_state64_get_sp(*state));
+
+ err = unw_init_local(&curs, &unwstate);
+ testassert(!err);
+
+ step = unw_step(&curs);
+ testassert(step > 0);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X19, ®);
+ testassert(!err);
+ testassert(reg == x19);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X20, ®);
+ testassert(!err);
+ testassert(reg == x20);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X21, ®);
+ testassert(!err);
+ testassert(reg == x21);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X22, ®);
+ testassert(!err);
+ testassert(reg == x22);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X23, ®);
+ testassert(!err);
+ testassert(reg == x23);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X24, ®);
+ testassert(!err);
+ testassert(reg == x24);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X25, ®);
+ testassert(!err);
+ testassert(reg == x25);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X26, ®);
+ testassert(!err);
+ testassert(reg == x26);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X27, ®);
+ testassert(!err);
+ testassert(reg == x27);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X28, ®);
+ testassert(!err);
+ testassert(reg == x28);
+
+ err = unw_get_reg(&curs, UNW_ARM64_FP, ®);
+ testassert(!err);
+ testassert(reg == fp);
+
+ err = unw_get_reg(&curs, UNW_ARM64_SP, ®);
+ testassert(!err);
+ testassert(reg == sp);
+
+ err = unw_get_reg(&curs, UNW_REG_IP, ®);
+ testassert(!err);
+ // libunwind's return is signed but our value is not
+ reg = (uintptr_t)ptrauth_strip((void *)reg, ptrauth_key_return_address);
+ testassert(reg == pc);
+
+ // libunwind restores PC into LR and doesn't track LR
+ // err = unw_get_reg(&curs, UNW_ARM64_LR, ®);
+ // testassert(!err);
+ // testassert(reg == lr);
+
+ // set signal handler's thread state to unwound state
+ state->__x[19] = x19;
+ state->__x[20] = x20;
+ state->__x[21] = x21;
+ state->__x[22] = x22;
+ state->__x[23] = x23;
+ state->__x[24] = x24;
+ state->__x[25] = x25;
+ state->__x[26] = x26;
+ state->__x[27] = x27;
+ state->__x[28] = x28;
+ state->__opaque_fp = (void *)fp;
+ state->__opaque_lr = (void *)pc; // libunwind restores PC into LR
+ state->__opaque_sp = (void *)sp;
+ state->__opaque_pc = (void *)pc;
+
+ caught = true;
+}
+
+
+void break_handler(int sig, siginfo_t *info, void *cc)
+{
+ ucontext_t *uc = (ucontext_t *)cc;
+ struct __darwin_mcontext64 *mc = (struct __darwin_mcontext64 *)uc->uc_mcontext;
+
+ testprintf(" handled\n");
+
+ testassert(sig == BREAK_SIGNAL);
+ testassert((uintptr_t)info->si_addr == clobbered);
+
+ handle_exception(&mc->__ss);
+ // handle_exception changed register state for continuation
+}
+
+
+__asm__(
+"\n .text"
+"\n .globl _callit"
+"\n _callit:"
+// save sp and return address to variables
+"\n mov x16, sp"
+"\n adrp x17, _sp@PAGE"
+"\n str x16, [x17, _sp@PAGEOFF]"
+"\n adrp x17, _pc@PAGE"
+"\n str lr, [x17, _pc@PAGEOFF]"
+// save other non-volatile registers to variables
+"\n adrp x17, _x19@PAGE"
+"\n str x19, [x17, _x19@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x20, [x17, _x20@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x21, [x17, _x21@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x22, [x17, _x22@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x23, [x17, _x23@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x24, [x17, _x24@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x25, [x17, _x25@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x26, [x17, _x26@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x27, [x17, _x27@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x28, [x17, _x28@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str fp, [x17, _fp@PAGEOFF]"
+"\n br x2"
+ );
+
+
+// arm64
+#elif __arm__
+
+#include <sys/ucontext.h>
+
+typedef uint16_t insn_t;
+typedef struct {
+ insn_t first;
+ insn_t second;
+ bool thirty_two;
+} clobbered_insn_t;
+#define BREAK_INSN ((insn_t)0xdefe) // trap
+#define BREAK_SIGNAL SIGILL
+#define BREAK_SIGNAL2 SIGTRAP
+
+uintptr_t r4 = 0;
+uintptr_t r5 = 0;
+uintptr_t r6 = 0;
+uintptr_t r7 = 0;
+uintptr_t r8 = 0;
+uintptr_t r10 = 0;
+uintptr_t r11 = 0;
+uintptr_t sp = 0;
+uintptr_t pc = 0;
+
+void handle_exception(arm_thread_state_t *state)
+{
+ // No unwind tables on this architecture so no libunwind checks.
+ // We run the test anyway to verify instruction-level coverage.
+
+ // set thread state to unwound state
+ state->__r[4] = r4;
+ state->__r[5] = r5;
+ state->__r[6] = r6;
+ state->__r[7] = r7;
+ state->__r[8] = r8;
+ state->__r[10] = r10;
+ state->__r[11] = r11;
+ state->__sp = sp;
+ state->__pc = pc;
+ // clear IT... bits so caller doesn't act on them
+ state->__cpsr &= ~0x0600fc00;
+
+ caught = true;
+}
+
+
+void break_handler(int sig, siginfo_t *info, void *cc)
+{
+ ucontext_t *uc = (ucontext_t *)cc;
+ struct __darwin_mcontext32 *mc = (struct __darwin_mcontext32 *)uc->uc_mcontext;
+
+ testprintf(" handled\n");
+
+ testassert(sig == BREAK_SIGNAL || sig == BREAK_SIGNAL2);
+ testassert((uintptr_t)info->si_addr == clobbered);
+
+ handle_exception(&mc->__ss);
+ // handle_exception changed register state for continuation
+}
+
+
+__asm__(
+"\n .text"
+"\n .syntax unified"
+"\n .code 16"
+"\n .align 5"
+"\n .globl _callit"
+"\n .thumb_func"
+"\n _callit:"
+// save sp and return address to variables
+"\n movw r12, :lower16:(_sp-1f-4)"
+"\n movt r12, :upper16:(_sp-1f-4)"
+"\n 1: add r12, pc"
+"\n str sp, [r12]"
+"\n movw r12, :lower16:(_pc-1f-4)"
+"\n movt r12, :upper16:(_pc-1f-4)"
+"\n 1: add r12, pc"
+"\n str lr, [r12]"
+// save other non-volatile registers to variables
+"\n movw r12, :lower16:(_r4-1f-4)"
+"\n movt r12, :upper16:(_r4-1f-4)"
+"\n 1: add r12, pc"
+"\n str r4, [r12]"
+"\n movw r12, :lower16:(_r5-1f-4)"
+"\n movt r12, :upper16:(_r5-1f-4)"
+"\n 1: add r12, pc"
+"\n str r5, [r12]"
+"\n movw r12, :lower16:(_r6-1f-4)"
+"\n movt r12, :upper16:(_r6-1f-4)"
+"\n 1: add r12, pc"
+"\n str r6, [r12]"
+"\n movw r12, :lower16:(_r7-1f-4)"
+"\n movt r12, :upper16:(_r7-1f-4)"
+"\n 1: add r12, pc"
+"\n str r7, [r12]"
+"\n movw r12, :lower16:(_r8-1f-4)"
+"\n movt r12, :upper16:(_r8-1f-4)"
+"\n 1: add r12, pc"
+"\n str r8, [r12]"
+"\n movw r12, :lower16:(_r10-1f-4)"
+"\n movt r12, :upper16:(_r10-1f-4)"
+"\n 1: add r12, pc"
+"\n str r10, [r12]"
+"\n movw r12, :lower16:(_r11-1f-4)"
+"\n movt r12, :upper16:(_r11-1f-4)"
+"\n 1: add r12, pc"
+"\n str r11, [r12]"
+"\n bx r2"
+ );
+
+__asm__(
+"\n .text"
+"\n .syntax unified"
+"\n .code 16"
+"\n .align 5"
+"\n .globl _callit_stret"
+"\n .thumb_func"
+"\n _callit_stret:"
+// save sp and return address to variables
+"\n movw r12, :lower16:(_sp-1f-4)"
+"\n movt r12, :upper16:(_sp-1f-4)"
+"\n 1: add r12, pc"
+"\n str sp, [r12]"
+"\n movw r12, :lower16:(_pc-1f-4)"
+"\n movt r12, :upper16:(_pc-1f-4)"
+"\n 1: add r12, pc"
+"\n str lr, [r12]"
+// save other non-volatile registers to variables
+"\n movw r12, :lower16:(_r4-1f-4)"
+"\n movt r12, :upper16:(_r4-1f-4)"
+"\n 1: add r12, pc"
+"\n str r4, [r12]"
+"\n movw r12, :lower16:(_r5-1f-4)"
+"\n movt r12, :upper16:(_r5-1f-4)"
+"\n 1: add r12, pc"
+"\n str r5, [r12]"
+"\n movw r12, :lower16:(_r6-1f-4)"
+"\n movt r12, :upper16:(_r6-1f-4)"
+"\n 1: add r12, pc"
+"\n str r6, [r12]"
+"\n movw r12, :lower16:(_r7-1f-4)"
+"\n movt r12, :upper16:(_r7-1f-4)"
+"\n 1: add r12, pc"
+"\n str r7, [r12]"
+"\n movw r12, :lower16:(_r8-1f-4)"
+"\n movt r12, :upper16:(_r8-1f-4)"
+"\n 1: add r12, pc"
+"\n str r8, [r12]"
+"\n movw r12, :lower16:(_r10-1f-4)"
+"\n movt r12, :upper16:(_r10-1f-4)"
+"\n 1: add r12, pc"
+"\n str r10, [r12]"
+"\n movw r12, :lower16:(_r11-1f-4)"
+"\n movt r12, :upper16:(_r11-1f-4)"
+"\n 1: add r12, pc"
+"\n str r11, [r12]"
+"\n bx r3"
+ );
+
+
+// arm
+#else
+
+#error unknown architecture
+
+#endif
+
+
+#if __arm__
+uintptr_t fnaddr(void *fn) { return (uintptr_t)fn & ~(uintptr_t)1; }
+#else
+uintptr_t fnaddr(void *fn) { return (uintptr_t)fn; }
+#endif
+
+insn_t set(uintptr_t dst, insn_t newvalue)
+{
+ uintptr_t start = dst & ~(PAGE_MAX_SIZE-1);
+ int err = mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_WRITE);
+ if (err) fail("mprotect(%p, RW-) failed (%d)", start, errno);
+ insn_t oldvalue = *(insn_t *)dst;
+ *(insn_t *)dst = newvalue;
+ err = mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_EXEC);
+ if (err) fail("mprotect(%p, R-X) failed (%d)", start, errno);
+ return oldvalue;
+}
+
+clobbered_insn_t clobber(void *fn, uintptr_t offset)
+{
+ clobbered = fnaddr(fn) + offset;
+ insn_t oldInsn = set(fnaddr(fn) + offset, BREAK_INSN);
+#if __arm__
+ // Need to clobber 32-bit Thumb instructions with another 32-bit instruction
+ // to preserve the behavior of IT... blocks.
+ clobbered_insn_t result = {oldInsn, 0, false};
+ if (((oldInsn & 0xf000) == 0xf000) ||
+ ((oldInsn & 0xf800) == 0xe800))
+ {
+ testprintf("clobbering thumb-32 at offset %zu\n", offset);
+ // Old insn was 32-bit. Clobber all of it.
+ // First unclobber.
+ set(fnaddr(fn) + offset, oldInsn);
+ // f7f0 a0f0 is a "permanently undefined" Thumb-2 instruction.
+ // Clobber the first half last so `clobbered` gets the right value.
+ result.second = set(fnaddr(fn) + offset + 2, 0xa0f0);
+ result.first = set(fnaddr(fn) + offset, 0xf7f0);
+ result.thirty_two = true;
+ }
+ return result;
+#else
+ return oldInsn;
+#endif
+}
+
+void unclobber(void *fn, uintptr_t offset, clobbered_insn_t oldvalue)
+{
+#if __arm__
+ if (oldvalue.thirty_two) {
+ set(fnaddr(fn) + offset + 2, oldvalue.second);
+ }
+ set(fnaddr(fn) + offset, oldvalue.first);
+#else
+ set(fnaddr(fn) + offset, oldvalue);
+#endif
+}
+
+
+// terminator for the list of instruction offsets
+#define END_OFFSETS ~0UL
+
+// Disassemble instructions symbol..<symbolEnd.
+// Write the offset of each non-NOP instruction start to *offsets..<end.
+// Return the incremented offsets pointer.
+uintptr_t *disassemble(uintptr_t symbol, uintptr_t symbolEnd,
+ uintptr_t *offsets, uintptr_t *end)
+{
+ // To disassemble:
+ // 1. Copy asm-placeholder.exe into a temporary file.
+ // 2. Write the instructions into the temp file.
+ // 3. Run llvm-objdump on the temp file.
+ // 4. Parse the llvm-objdump output.
+
+ // copy asm-placeholder.exe into a new temporary file and open it.
+ int placeholder = open("asm-placeholder.exe", O_RDONLY);
+ if (placeholder < 0) {
+ fail("couldn't open asm-placeholder.exe (%d)", errno);
+ }
+
+ size_t tempdirlen = confstr(_CS_DARWIN_USER_TEMP_DIR, nil, 0);
+ char tempsuffix[] = "objc-test-msgSend-asm-XXXXXX";
+ char *tempname = (char *)malloc(tempdirlen + strlen(tempsuffix));
+ confstr(_CS_DARWIN_USER_TEMP_DIR, tempname, tempdirlen);
+ strcat(tempname, tempsuffix);
+
+ int fd = mkstemp(tempname);
+ if (fd < 0) {
+ fail("couldn't create asm temp file %s (%d)", tempname, errno);
+ }
+ struct stat st;
+ if (fstat(placeholder, &st) < 0) {
+ fail("couldn't stat asm-placeholder.exe (%d)", errno);
+ }
+ char *buf = (char *)malloc(st.st_size);
+ if (pread(placeholder, buf, st.st_size, 0) != st.st_size) {
+ fail("couldn't read asm-placeholder.exe (%d)", errno);
+ }
+ if (pwrite(fd, buf, st.st_size, 0) != st.st_size) {
+ fail("couldn't write asm temp file %s (%d)", tempname, errno);
+ }
+ free(buf);
+ close(placeholder);
+
+ // write code into asm-placeholder.exe
+ // asm-placeholder.exe may have as little as 1024 bytes of space reserved
+ testassert(symbolEnd - symbol < 1024);
+ // text section should be 16KB into asm-placeholder.exe
+ if (pwrite(fd, (void*)symbol, symbolEnd - symbol, 16384) < 0) {
+ fail("couldn't write code into asm temp file %s (%d)", tempname, errno);
+ }
+ close(fd);
+
+ // run `llvm-objdump -disassemble`
+ const char *objdump;
+ if (0 == access("/usr/local/bin/llvm-objdump", F_OK)) {
+ objdump = "/usr/local/bin/llvm-objdump";
+ } else if (0 == access("/usr/bin/llvm-objdump", F_OK)) {
+ objdump = "/usr/bin/llvm-objdump";
+ } else {
+ fail("couldn't find llvm-objdump");
+ }
+ char *cmd;
+ asprintf(&cmd, "%s -disassemble %s", objdump, tempname);
+ FILE *disa = popen(cmd, "r");
+ if (!disa) {
+ fail("couldn't popen %s", cmd);
+ }
+ free(cmd);
+ free(tempname);
+
+ // read past "_main:" line
+ char *line;
+ size_t len;
+ while ((line = fgetln(disa, &len))) {
+ testprintf("ASM: %.*s", (int)len, line);
+ if (0 == strncmp(line, "_main:", strlen("_main:"))) break;
+ }
+
+ // read instructions and save offsets
+ char op[128];
+ long base = 0;
+ long addr;
+ uintptr_t *p = offsets;
+ // disassembly format:
+ // ADDR:\t ...instruction bytes... \tOPCODE ...etc...\n
+ while (2 == fscanf(disa, "%lx:\t%*[a-fA-F0-9 ]\t%s%*[^\n]\n", &addr, op)) {
+ if (base == 0) base = addr;
+ testprintf("ASM: %lx (+%d) ... %s ...\n", addr, addr - base, op);
+ // allow longer nops like Intel nopw and nopl
+ if (0 != strncmp(op, "nop", 3)) {
+ testassert(offsets < end);
+ *p++ = addr - base;
+ } else {
+ // assume nops are unreached (e.g. alignment padding)
+ }
+ }
+ pclose(disa);
+
+#if __x86_64__
+ // hack: skip last instruction because libunwind blows up if it's
+ // one byte long and followed by the next function with no NOPs first
+ if (p > offsets) *p-- = END_OFFSETS;
+#endif
+
+ return p;
+}
+
+
+uintptr_t *getOffsets(const char *symname, uintptr_t *outBase)
+{
+ // Find the start of our function.
+ uintptr_t symbol = (uintptr_t)dlsym(RTLD_NEXT, symname);
+ if (!symbol) return nil;
+#if __has_feature(ptrauth_calls)
+ symbol = (uintptr_t)
+ ptrauth_strip((void*)symbol, ptrauth_key_function_pointer);
+#endif
+
+ if (outBase) *outBase = symbol;
+
+ // Find the end of our function by finding the start
+ // of the next symbol after our target symbol.
+
+ const int insnIncrement =
+#if __arm64__
+ 4;
+#elif __arm__
+ 2; // in case of thumb or thumb-2
+#elif __i386__ || __x86_64__
+ 1;
+#else
+#error unknown architecture
+#endif
+
+ uintptr_t symbolEnd;
+ Dl_info dli;
+ int ok;
+ for (symbolEnd = symbol + insnIncrement;
+ ((ok = dladdr((void*)symbolEnd, &dli))) && dli.dli_saddr == (void*)symbol;
+ symbolEnd += insnIncrement)
+ ;
+
+ testprintf("found %s at %p..<%p %d %p %s\n",
+ symname, (void*)symbol, (void*)symbolEnd, ok, dli.dli_saddr, dli.dli_sname);
+
+ // Record the offset to each non-NOP instruction.
+ uintptr_t *result = (uintptr_t *)malloc(1000 * sizeof(uintptr_t));
+ uintptr_t *end = result + 1000;
+ uintptr_t *p = result;
+
+ p = disassemble(symbol, symbolEnd, p, end);
+
+ // Also record the offsets in _objc_msgSend_uncached when present
+ // (which is the slow path and has a frame to unwind)
+ if (!strstr(symname, "_uncached")) {
+ const char *uncached_symname = strstr(symname, "stret")
+ ? "_objc_msgSend_stret_uncached" : "_objc_msgSend_uncached";
+ uintptr_t uncached_symbol;
+ uintptr_t *uncached_offsets =
+ getOffsets(uncached_symname, &uncached_symbol);
+ if (uncached_offsets) {
+ uintptr_t *q = uncached_offsets;
+ // Skip prologue and epilogue of objc_msgSend_uncached
+ // because it's imprecisely modeled in compact unwind
+ int prologueInstructions, epilogueInstructions;
+#if __arm64e__
+ prologueInstructions = 3;
+ epilogueInstructions = 2;
+#elif __arm64__ || __x86_64__ || __i386__ || __arm__
+ prologueInstructions = 2;
+ epilogueInstructions = 1;
+#else
+#error unknown architecture
+#endif
+ // skip past prologue
+ for (int i = 0; i < prologueInstructions; i++) {
+ testassert(*q != END_OFFSETS);
+ q++;
+ }
+
+ // copy instructions
+ while (*q != END_OFFSETS) *p++ = *q++ + uncached_symbol - symbol;
+
+ // rewind past epilogue
+ for (int i = 0; i < epilogueInstructions; i++) {
+ testassert(p > result);
+ p--;
+ }
+
+ free(uncached_offsets);
+ }
+ }
+
+ // Terminate the list of offsets and return.
+ testassert(p > result);
+ testassert(p < end);
+ *p = END_OFFSETS;
+
+ return result;
+}
+
+
+void CALLIT(void *o, void *sel_arg, SEL s, void *f, bool stret) __attribute__((noinline));
+void CALLIT(void *o, void *sel_arg, SEL s, void *f, bool stret)
+{
+ uintptr_t message_ref[2];
+ if (sel_arg != s) {
+ // fixup dispatch
+ // copy to a local buffer to keep sel_arg un-fixed-up
+ memcpy(message_ref, sel_arg, sizeof(message_ref));
+ sel_arg = message_ref;
+ }
+ if (!stret) callit(o, sel_arg, f);
+#if SUPPORT_STRET
+ else callit_stret(o, sel_arg, f);
+#else
+ else fail("stret?");
+#endif
+}
+
+void test_dw_forward(void)
+{
+ return;
+}
+
+struct stret test_dw_forward_stret(void)
+{
+ return zero;
+}
+
+// sub = ordinary receiver object
+// tagged = tagged receiver object
+// SEL = selector to send
+// sub_arg = arg to pass in receiver register (may be objc_super struct)
+// tagged_arg = arg to pass in receiver register (may be objc_super struct)
+// sel_arg = arg to pass in sel register (may be message_ref)
+// uncaughtAllowed is the number of acceptable unreachable instructions
+// (for example, the ones that handle the corrupt-cache-error case)
+void test_dw(const char *name, id sub, id tagged, id exttagged, bool stret,
+ int uncaughtAllowed)
+{
+
+ testprintf("DWARF FOR %s%s\n", name, stret ? " (stret)" : "");
+
+ // We need 2 SELs of each alignment so we can generate hash collisions.
+ // sel_registerName() never returns those alignments because they
+ // differ from malloc's alignment. So we create lots of compiled-in
+ // SELs here and hope something fits.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+ SEL sel = @selector(a);
+ SEL lotsOfSels[] = {
+ @selector(a1), @selector(a2), @selector(a3), @selector(a4),
+ @selector(a5), @selector(a6), @selector(a7), @selector(a8),
+ @selector(aa), @selector(ab), @selector(ac), @selector(ad),
+ @selector(ae), @selector(af), @selector(ag), @selector(ah),
+ @selector(A1), @selector(A2), @selector(A3), @selector(A4),
+ @selector(A5), @selector(A6), @selector(A7), @selector(A8),
+ @selector(AA), @selector(Ab), @selector(Ac), @selector(Ad),
+ @selector(Ae), @selector(Af), @selector(Ag), @selector(Ah),
+ @selector(bb1), @selector(bb2), @selector(bb3), @selector(bb4),
+ @selector(bb5), @selector(bb6), @selector(bb7), @selector(bb8),
+ @selector(bba), @selector(bbb), @selector(bbc), @selector(bbd),
+ @selector(bbe), @selector(bbf), @selector(bbg), @selector(bbh),
+ @selector(BB1), @selector(BB2), @selector(BB3), @selector(BB4),
+ @selector(BB5), @selector(BB6), @selector(BB7), @selector(BB8),
+ @selector(BBa), @selector(BBb), @selector(BBc), @selector(BBd),
+ @selector(BBe), @selector(BBf), @selector(BBg), @selector(BBh),
+ @selector(ccc1), @selector(ccc2), @selector(ccc3), @selector(ccc4),
+ @selector(ccc5), @selector(ccc6), @selector(ccc7), @selector(ccc8),
+ @selector(ccca), @selector(cccb), @selector(cccc), @selector(cccd),
+ @selector(ccce), @selector(cccf), @selector(cccg), @selector(ccch),
+ @selector(CCC1), @selector(CCC2), @selector(CCC3), @selector(CCC4),
+ @selector(CCC5), @selector(CCC6), @selector(CCC7), @selector(CCC8),
+ @selector(CCCa), @selector(CCCb), @selector(CCCc), @selector(CCCd),
+ @selector(CCCe), @selector(CCCf), @selector(CCCg), @selector(CCCh),
+ };
+#pragma clang diagnostic pop
+
+ {
+ IMP imp = stret ? (IMP)test_dw_forward_stret : (IMP)test_dw_forward;
+ Class cls = object_getClass(sub);
+ Class tagcls = object_getClass(tagged);
+ Class exttagcls = object_getClass(exttagged);
+ class_replaceMethod(cls, sel, imp, "");
+ class_replaceMethod(tagcls, sel, imp, "");
+ class_replaceMethod(exttagcls, sel, imp, "");
+ for (size_t i = 0; i < sizeof(lotsOfSels)/sizeof(lotsOfSels[0]); i++) {
+ class_replaceMethod(cls, lotsOfSels[i], imp, "");
+ class_replaceMethod(tagcls, lotsOfSels[i], imp, "");
+ class_replaceMethod(exttagcls, lotsOfSels[i], imp, "");
+ }
+ }
+
+ #define ALIGNCOUNT 16
+ SEL sels[ALIGNCOUNT][2] = {{0}};
+ for (int align = 0; align < ALIGNCOUNT; align++) {
+ for (size_t i = 0; i < sizeof(lotsOfSels)/sizeof(lotsOfSels[0]); i++) {
+ if ((uintptr_t)(void*)lotsOfSels[i] % ALIGNCOUNT == align) {
+ if (sels[align][0]) {
+ sels[align][1] = lotsOfSels[i];
+ } else {
+ sels[align][0] = lotsOfSels[i];
+ }
+ }
+ }
+ if (!sels[align][0]) fail("no SEL with alignment %d", align);
+ if (!sels[align][1]) fail("only one SEL with alignment %d", align);
+ }
+
+ void *fn = dlsym(RTLD_DEFAULT, name);
+#if __has_feature(ptrauth_calls)
+ fn = ptrauth_strip(fn, ptrauth_key_function_pointer);
+#endif
+ testassert(fn);
+
+ // argument substitutions
+
+ void *sub_arg = (__bridge void*)sub;
+ void *tagged_arg = (__bridge void*)tagged;
+ void *exttagged_arg = (__bridge void*)exttagged;
+ void *sel_arg = (void*)sel;
+
+ struct objc_super sup_st = { sub, object_getClass(sub) };
+ struct objc_super tagged_sup_st = { tagged, object_getClass(tagged) };
+ struct objc_super exttagged_sup_st = { exttagged, object_getClass(exttagged) };
+ struct { void *imp; SEL sel; } message_ref = { fn, sel };
+
+ Class cache_cls = object_getClass(sub);
+ Class tagged_cache_cls = object_getClass(tagged);
+ Class exttagged_cache_cls = object_getClass(exttagged);
+
+ if (strstr(name, "Super")) {
+ // super version - replace receiver with objc_super
+ // clear caches of superclass
+ cache_cls = class_getSuperclass(cache_cls);
+ tagged_cache_cls = class_getSuperclass(tagged_cache_cls);
+ exttagged_cache_cls = class_getSuperclass(exttagged_cache_cls);
+ sub_arg = &sup_st;
+ tagged_arg = &tagged_sup_st;
+ exttagged_arg = &exttagged_sup_st;
+ }
+
+ if (strstr(name, "_fixup")) {
+ // fixup version - replace sel with message_ref
+ sel_arg = &message_ref;
+ }
+
+
+ uintptr_t *insnOffsets = getOffsets(name, nil);
+ testassert(insnOffsets);
+ uintptr_t offset;
+ int uncaughtCount = 0;
+ for (int oo = 0; insnOffsets[oo] != ~0UL; oo++) {
+ offset = insnOffsets[oo];
+ testprintf("OFFSET %lu\n", offset);
+
+ clobbered_insn_t saved_insn = clobber(fn, offset);
+ caught = false;
+
+ // nil
+ if ((__bridge void*)sub == sub_arg) {
+ SELF = nil;
+ testprintf(" nil\n");
+ CALLIT(nil, sel_arg, sel, fn, stret);
+ CALLIT(nil, sel_arg, sel, fn, stret);
+ }
+
+ // uncached
+ SELF = sub;
+ testprintf(" uncached\n");
+ _objc_flush_caches(cache_cls);
+ CALLIT(sub_arg, sel_arg, sel, fn, stret);
+ _objc_flush_caches(cache_cls);
+ CALLIT(sub_arg, sel_arg, sel, fn, stret);
+
+ // cached
+ SELF = sub;
+ testprintf(" cached\n");
+ CALLIT(sub_arg, sel_arg, sel, fn, stret);
+ CALLIT(sub_arg, sel_arg, sel, fn, stret);
+
+ // uncached,tagged
+ SELF = tagged;
+ testprintf(" uncached,tagged\n");
+ _objc_flush_caches(tagged_cache_cls);
+ CALLIT(tagged_arg, sel_arg, sel, fn, stret);
+ _objc_flush_caches(tagged_cache_cls);
+ CALLIT(tagged_arg, sel_arg, sel, fn, stret);
+ _objc_flush_caches(exttagged_cache_cls);
+ CALLIT(exttagged_arg, sel_arg, sel, fn, stret);
+ _objc_flush_caches(exttagged_cache_cls);
+ CALLIT(exttagged_arg, sel_arg, sel, fn, stret);
+
+ // cached,tagged
+ SELF = tagged;
+ testprintf(" cached,tagged\n");
+ CALLIT(tagged_arg, sel_arg, sel, fn, stret);
+ CALLIT(tagged_arg, sel_arg, sel, fn, stret);
+ CALLIT(exttagged_arg, sel_arg, sel, fn, stret);
+ CALLIT(exttagged_arg, sel_arg, sel, fn, stret);
+
+ // multiple SEL alignments, collisions, wraps
+ SELF = sub;
+ for (int a = 0; a < ALIGNCOUNT; a++) {
+ testprintf(" cached and uncached, SEL alignment %d\n", a);
+
+ // Count both up and down to be independent of
+ // implementation's cache scan direction
+
+ _objc_flush_caches(cache_cls);
+ for (int x2 = 0; x2 < 8; x2++) {
+ for (int s = 0; s < 4; s++) {
+ int align = (a+s) % ALIGNCOUNT;
+ CALLIT(sub_arg, sels[align][0], sels[align][0], fn, stret);
+ CALLIT(sub_arg, sels[align][1], sels[align][1], fn, stret);
+ }
+ }
+
+ _objc_flush_caches(cache_cls);
+ for (int x2 = 0; x2 < 8; x2++) {
+ for (int s = 0; s < 4; s++) {
+ int align = abs(a-s) % ALIGNCOUNT;
+ CALLIT(sub_arg, sels[align][0], sels[align][0], fn, stret);
+ CALLIT(sub_arg, sels[align][1], sels[align][1], fn, stret);
+ }
+ }
+ }
+
+ unclobber(fn, offset, saved_insn);
+
+ // remember offsets that were caught by none of the above
+ if (caught) {
+ insnOffsets[oo] = 0;
+ } else {
+ uncaughtCount++;
+ testprintf("offset %s+%lu not caught (%d/%d)\n",
+ name, offset, uncaughtCount, uncaughtAllowed);
+ }
+ }
+
+ // Complain if too many offsets went uncaught.
+ // Acceptably-uncaught offsets include the corrupt-cache-error handler.
+ if (uncaughtCount != uncaughtAllowed) {
+ for (int oo = 0; insnOffsets[oo] != ~0UL; oo++) {
+ if (insnOffsets[oo]) {
+ fprintf(stderr, "BAD: offset %s+%lu not caught\n",
+ name, insnOffsets[oo]);
+ }
+ }
+ fail("wrong instructions not reached for %s (missed %d, expected %d)",
+ name, uncaughtCount, uncaughtAllowed);
+ }
+
+ free(insnOffsets);
+}
+
+
+// TEST_DWARF
+#endif
+
+
+void test_basic(id receiver)
+{
+ id idval;
+ long long llval;
+ struct stret stretval;
+ double fpval;
+ long double lfpval;
+ vector_ulong2 vecval;
+
+ // message uncached
+ // message uncached long long
+ // message uncached stret
+ // message uncached fpret
+ // message uncached fpret long double
+ // message uncached noarg (as above)
+ // message cached
+ // message cached long long
+ // message cached stret
+ // message cached fpret
+ // message cached fpret long double
+ // message cached noarg (as above)
+ // fixme verify that uncached lookup didn't happen the 2nd time?
+ SELF = receiver;
+ _objc_flush_caches(object_getClass(receiver));
+ for (int i = 0; i < 5; i++) {
+ testprintf("idret\n");
+ state = 0;
+ idval = nil;
+ idval = [receiver idret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 101);
+ testassert(idval == ID_RESULT);
+
+ testprintf("llret\n");
+ llval = 0;
+ llval = [receiver llret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 102);
+ testassert(llval == LL_RESULT);
+
+ testprintf("stret\n");
+ stretval = zero;
+ stretval = [receiver stret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 103);
+ testassert(stret_equal(stretval, STRET_RESULT));
+
+ testprintf("fpret\n");
+ fpval = 0;
+ fpval = [receiver fpret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 104);
+ testassert(fpval == FP_RESULT);
+
+ testprintf("lfpret\n");
+ lfpval = 0;
+ lfpval = [receiver lfpret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 105);
+ testassert(lfpval == LFP_RESULT);
+
+ testprintf("vecret\n");
+ vecval = 0;
+ vecval = [receiver vecret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 106);
+ testassert(vector_equal(vecval, VEC_RESULT));
+
+ // explicitly call noarg messenger, even if compiler doesn't emit it
+ state = 0;
+ testprintf("idret noarg\n");
+ idval = nil;
+ idval = ((typeof(idmsg0))objc_msgSend_noarg)(receiver, @selector(idret_noarg));
+ testassert(state == 111);
+ testassert(idval == ID_RESULT);
+
+ testprintf("llret noarg\n");
+ llval = 0;
+ llval = ((typeof(llmsg0))objc_msgSend_noarg)(receiver, @selector(llret_noarg));
+ testassert(state == 112);
+ testassert(llval == LL_RESULT);
+ /*
+ no objc_msgSend_stret_noarg
+ stretval = zero;
+ stretval = ((typeof(stretmsg0))objc_msgSend_stret_noarg)(receiver, @selector(stret_noarg));
+ stretval = [receiver stret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 113);
+ testassert(stret_equal(stretval, STRET_RESULT));
+ */
+#if !__i386__
+ testprintf("fpret noarg\n");
+ fpval = 0;
+ fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(receiver, @selector(fpret_noarg));
+ testassert(state == 114);
+ testassert(fpval == FP_RESULT);
+
+ testprintf("vecret noarg\n");
+ vecval = 0;
+ vecval = ((typeof(vecmsg0))objc_msgSend_noarg)(receiver, @selector(vecret_noarg));
+ testassert(state == 116);
+ testassert(vector_equal(vecval, VEC_RESULT));
+#endif
+#if !__i386__ && !__x86_64__
+ testprintf("lfpret noarg\n");
+ lfpval = 0;
+ lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)(receiver, @selector(lfpret_noarg));
+ testassert(state == 115);
+ testassert(lfpval == LFP_RESULT);
+#endif
+ }
+
+ testprintf("basic done\n");
+}
+
+int main()
+{
+ PUSH_POOL {
+ id idval;
+ long long llval;
+ struct stret stretval;
+ double fpval;
+ long double lfpval;
+ vector_ulong2 vecval;
+
+#if __x86_64__
+ struct stret *stretptr;
+#endif
+
+ Method idmethod;
+ Method llmethod;
+ Method stretmethod;
+ Method fpmethod;
+ Method lfpmethod;
+ Method vecmethod;
+
+ id (*idfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
+ long long (*llfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
+ struct stret (*stretfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
+ double (*fpfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
+ long double (*lfpfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
+ vector_ulong2 (*vecfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
+
+ id (*idmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ id (*idmsgsuper)(struct objc_super *, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ long long (*llmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ struct stret (*stretmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ struct stret (*stretmsgsuper)(struct objc_super *, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ double (*fpmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ long double (*lfpmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ vector_ulong2 (*vecmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+
+ // get +initialize out of the way
+ [Sub class];
+#if OBJC_HAVE_TAGGED_POINTERS
+ [TaggedSub class];
+ [ExtTaggedSub class];
+#endif
+
+ ID_RESULT = [Super new];
+
+ Sub *sub = [Sub new];
+ Super *sup = [Super new];
+#if OBJC_HAVE_TAGGED_POINTERS
+ TaggedSub *tagged = (__bridge id)_objc_makeTaggedPointer(OBJC_TAG_1, 999);
+ ExtTaggedSub *exttagged = (__bridge id)_objc_makeTaggedPointer(OBJC_TAG_First52BitPayload, 999);
+#endif
+
+ // Basic cached and uncached dispatch.
+ // Do this first before anything below caches stuff.
+ testprintf("basic\n");
+ test_basic(sub);
+#if OBJC_HAVE_TAGGED_POINTERS
+ testprintf("basic tagged\n");
+ test_basic(tagged);
+ testprintf("basic ext tagged\n");
+ test_basic(exttagged);
+#endif
+
+ idmethod = class_getInstanceMethod([Super class], @selector(idret::::::::::::::::::::::::::::::::::::));
+ testassert(idmethod);
+ llmethod = class_getInstanceMethod([Super class], @selector(llret::::::::::::::::::::::::::::::::::::));
+ testassert(llmethod);
+ stretmethod = class_getInstanceMethod([Super class], @selector(stret::::::::::::::::::::::::::::::::::::));
+ testassert(stretmethod);
+ fpmethod = class_getInstanceMethod([Super class], @selector(fpret::::::::::::::::::::::::::::::::::::));
+ testassert(fpmethod);
+ lfpmethod = class_getInstanceMethod([Super class], @selector(lfpret::::::::::::::::::::::::::::::::::::));
+ testassert(lfpmethod);
+ vecmethod = class_getInstanceMethod([Super class], @selector(vecret::::::::::::::::::::::::::::::::::::));
+ testassert(vecmethod);
+
+ idfn = (id (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+ llfn = (long long (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+ stretfn = (struct stret (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke_stret;
+ fpfn = (double (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+ lfpfn = (long double (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+ vecfn = (vector_ulong2 (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+
+ // method_invoke
+ // method_invoke long long
+ // method_invoke_stret stret
+ // method_invoke_stret fpret
+ // method_invoke fpret long double
+ testprintf("method_invoke\n");
+
+ SELF = sup;
+
+ state = 0;
+ idval = nil;
+ idval = (*idfn)(sup, idmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 1);
+ testassert(idval == ID_RESULT);
+
+ llval = 0;
+ llval = (*llfn)(sup, llmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 2);
+ testassert(llval == LL_RESULT);
+
+ stretval = zero;
+ stretval = (*stretfn)(sup, stretmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 3);
+ testassert(stret_equal(stretval, STRET_RESULT));
+
+ fpval = 0;
+ fpval = (*fpfn)(sup, fpmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 4);
+ testassert(fpval == FP_RESULT);
+
+ lfpval = 0;
+ lfpval = (*lfpfn)(sup, lfpmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 5);
+ testassert(lfpval == LFP_RESULT);
+
+ vecval = 0;
+ vecval = (*vecfn)(sup, vecmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 6);
+ testassert(vector_equal(vecval, VEC_RESULT));
+
+
+ // message to nil
+ // message to nil long long
+ // message to nil stret
+ // message to nil fpret
+ // message to nil fpret long double
+ // Use NIL_RECEIVER to avoid compiler optimizations.
+ testprintf("message to nil\n");
+
+ state = 0;
+ idval = ID_RESULT;
+ idval = [(id)NIL_RECEIVER idret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 0);
+ testassert(idval == nil);
+
+ state = 0;
+ llval = LL_RESULT;
+ llval = [(id)NIL_RECEIVER llret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 0);
+ testassert(llval == 0LL);
+
+ state = 0;
+ stretval = zero;
+ stretval = [(id)NIL_RECEIVER stret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 0);
+ testassert(0 == memcmp(&stretval, &zero, sizeof(stretval)));
+
+#if __x86_64__
+ // check stret return register
+ state = 0;
+ stretval = zero;
+ stretptr = ((struct stret *(*)(struct stret *, id, SEL))objc_msgSend_stret)
+ (&stretval, nil, @selector(stret_nop));
+ testassert(stretptr == &stretval);
+ testassert(state == 0);
+ // no stret result guarantee for hand-written calls
+#endif
+
+#if __i386__
+ // check struct-return address stack pop
+ for (int i = 0; i < 10000000; i++) {
+ state = 0;
+ ((struct stret (*)(id, SEL))objc_msgSend_stret)
+ (nil, @selector(stret_nop));
+ }
+#endif
+
+ state = 0;
+ fpval = FP_RESULT;
+ fpval = [(id)NIL_RECEIVER fpret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 0);
+ testassert(fpval == 0.0);
+
+ state = 0;
+ lfpval = LFP_RESULT;
+ lfpval = [(id)NIL_RECEIVER lfpret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 0);
+ testassert(lfpval == 0.0);
+
+ state = 0;
+ vecval = VEC_RESULT;
+ vecval = [(id)NIL_RECEIVER vecret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 0);
+ testassert(vector_all(vecval == 0));
+
+ // message to nil, different struct types
+ // This verifies that ordinary objc_msgSend() erases enough registers
+ // for structs that return in registers.
+#define TEST_NIL_STRUCT(i,n) \
+ do { \
+ struct stret_##i##n z; \
+ bzero(&z, sizeof(z)); \
+ [Super stret_i##n##_nonzero]; \
+ [Super stret_d##n##_nonzero]; \
+ struct stret_##i##n val = [(id)NIL_RECEIVER stret_##i##n##_zero]; \
+ testassert(0 == memcmp(&z, &val, sizeof(val))); \
+ } while (0)
+
+ TEST_NIL_STRUCT(i,1);
+ TEST_NIL_STRUCT(i,2);
+ TEST_NIL_STRUCT(i,3);
+ TEST_NIL_STRUCT(i,4);
+ TEST_NIL_STRUCT(i,5);
+ TEST_NIL_STRUCT(i,6);
+ TEST_NIL_STRUCT(i,7);
+ TEST_NIL_STRUCT(i,8);
+ TEST_NIL_STRUCT(i,9);
+
+#if __i386__
+ testwarn("rdar://16267205 i386 struct{float} and struct{double}");
+#else
+ TEST_NIL_STRUCT(d,1);
+#endif
+ TEST_NIL_STRUCT(d,2);
+ TEST_NIL_STRUCT(d,3);
+ TEST_NIL_STRUCT(d,4);
+ TEST_NIL_STRUCT(d,5);
+ TEST_NIL_STRUCT(d,6);
+ TEST_NIL_STRUCT(d,7);
+ TEST_NIL_STRUCT(d,8);
+ TEST_NIL_STRUCT(d,9);
+
+
+ // message to nil noarg
+ // explicitly call noarg messenger, even if compiler doesn't emit it
+ state = 0;
+ idval = ID_RESULT;
+ idval = ((typeof(idmsg0))objc_msgSend_noarg)(nil, @selector(idret_noarg));
+ testassert(state == 0);
+ testassert(idval == nil);
+
+ state = 0;
+ llval = LL_RESULT;
+ llval = ((typeof(llmsg0))objc_msgSend_noarg)(nil, @selector(llret_noarg));
+ testassert(state == 0);
+ testassert(llval == 0LL);
+
+ // no stret_noarg messenger
+
+#if !__i386__
+ state = 0;
+ fpval = FP_RESULT;
+ fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(nil, @selector(fpret_noarg));
+ testassert(state == 0);
+ testassert(fpval == 0.0);
+
+ state = 0;
+ vecval = VEC_RESULT;
+ vecval = ((typeof(vecmsg0))objc_msgSend_noarg)(nil, @selector(vecret_noarg));
+ testassert(state == 0);
+ testassert(vector_all(vecval == 0));
+#endif
+#if !__i386__ && !__x86_64__
+ state = 0;
+ lfpval = LFP_RESULT;
+ lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)(nil, @selector(lfpret_noarg));
+ testassert(state == 0);
+ testassert(lfpval == 0.0);
+#endif
+
+
+ // rdar://8271364 objc_msgSendSuper2 must not change objc_super
+ testprintf("super struct\n");
+ struct objc_super sup_st = {
+ sub,
+ object_getClass(sub),
+ };
+
+ SELF = sub;
+
+ state = 100;
+ idval = nil;
+ idval = ((id(*)(struct objc_super *, SEL, vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2) (&sup_st, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1,VEC2,VEC3,VEC4,VEC5,VEC6,VEC7,VEC8, 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0);
+ testassert(state == 1);
+ testassert(idval == ID_RESULT);
+ testassert(sup_st.receiver == sub);
+ testassert(sup_st.super_class == object_getClass(sub));
+
+ state = 100;
+ stretval = zero;
+ stretval = ((struct stret(*)(struct objc_super *, SEL, vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2_stret) (&sup_st, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1,VEC2,VEC3,VEC4,VEC5,VEC6,VEC7,VEC8, 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0);
+ testassert(state == 3);
+ testassert(stret_equal(stretval, STRET_RESULT));
+ testassert(sup_st.receiver == sub);
+ testassert(sup_st.super_class == object_getClass(sub));
+
+#if !__arm64__
+ // Debug messengers.
+ testprintf("debug messengers\n");
+
+ state = 0;
+ idmsg = (typeof(idmsg))objc_msgSend_debug;
+ idval = nil;
+ idval = (*idmsg)(sub, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 101);
+ testassert(idval == ID_RESULT);
+
+ state = 0;
+ llmsg = (typeof(llmsg))objc_msgSend_debug;
+ llval = 0;
+ llval = (*llmsg)(sub, @selector(llret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 102);
+ testassert(llval == LL_RESULT);
+
+ state = 0;
+ stretmsg = (typeof(stretmsg))objc_msgSend_stret_debug;
+ stretval = zero;
+ stretval = (*stretmsg)(sub, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 103);
+ testassert(stret_equal(stretval, STRET_RESULT));
+
+ state = 100;
+ sup_st.receiver = sub;
+ sup_st.super_class = object_getClass(sub);
+ idmsgsuper = (typeof(idmsgsuper))objc_msgSendSuper2_debug;
+ idval = nil;
+ idval = (*idmsgsuper)(&sup_st, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 1);
+ testassert(idval == ID_RESULT);
+
+ state = 100;
+ sup_st.receiver = sub;
+ sup_st.super_class = object_getClass(sub);
+ stretmsgsuper = (typeof(stretmsgsuper))objc_msgSendSuper2_stret_debug;
+ stretval = zero;
+ stretval = (*stretmsgsuper)(&sup_st, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 3);
+ testassert(stret_equal(stretval, STRET_RESULT));
+
+#if __i386__
+ state = 0;
+ fpmsg = (typeof(fpmsg))objc_msgSend_fpret_debug;
+ fpval = 0;
+ fpval = (*fpmsg)(sub, @selector(fpret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 104);
+ testassert(fpval == FP_RESULT);
+#endif
+#if __x86_64__
+ state = 0;
+ lfpmsg = (typeof(lfpmsg))objc_msgSend_fpret_debug;
+ lfpval = 0;
+ lfpval = (*lfpmsg)(sub, @selector(lfpret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 105);
+ testassert(lfpval == LFP_RESULT);
+
+ // fixme fp2ret
+#endif
+
+ // debug messengers
+#endif
+
+
+ // objc_msgLookup
+
+#if 1
+ // fixme objc_msgLookup test hack stopped working after a compiler update
+
+#elif __has_feature(objc_arc)
+ // ARC interferes with objc_msgLookup test hacks
+
+#elif __i386__ && TARGET_OS_SIMULATOR
+ testwarn("fixme msgLookup hack doesn't work");
+
+#else
+ // fixme hack: call the looked-up method
+# if __arm64__
+# define CALL_LOOKUP(ret) \
+ asm volatile ("blr x17 \n mov %x0, x0" : "=r" (ret))
+# define CALL_LOOKUP_STRET(ret) \
+ asm volatile ("mov x8, %x1 \n blr x17 \n" : "=m" (ret) : "r" (&ret))
+
+# elif __arm__
+# define CALL_LOOKUP(ret) \
+ asm volatile ("blx r12 \n mov %0, r0" : "=r" (ret))
+# define CALL_LOOKUP_STRET(ret) \
+ asm volatile ("mov r0, %1 \n blx r12 \n" : "=m" (ret) : "r" (&ret))
+
+# elif __x86_64__
+# define CALL_LOOKUP(ret) \
+ asm volatile ("call *%%r11 \n mov %%rax, %0" : "=r" (ret))
+# define CALL_LOOKUP_STRET(ret) \
+ asm volatile ("mov %1, %%rdi \n call *%%r11 \n" : "=m" (ret) : "r" (&ret))
+
+# elif __i386__
+# define CALL_LOOKUP(ret) \
+ asm volatile ("call *%%eax \n mov %%eax, %0" : "=r" (ret))
+# define CALL_LOOKUP_STRET(ret) \
+ asm volatile ("add $4, %%esp \n mov %1, (%%esp) \n call *%%eax \n sub $4, %%esp \n" : "=m" (ret) : "d" (&ret))
+
+# else
+# error unknown architecture
+# endif
+
+ // msgLookup uncached
+ // msgLookup uncached super
+ // msgLookup uncached stret
+ // msgLookup uncached super stret
+ // msgLookup uncached fpret
+ // msgLookup uncached fpret long double
+ // msgLookup cached
+ // msgLookup cached stret
+ // msgLookup cached super
+ // msgLookup cached super stret
+ // msgLookup cached fpret
+ // msgLookup cached fpret long double
+ // fixme verify that uncached lookup didn't happen the 2nd time?
+ SELF = sub;
+ _objc_flush_caches(object_getClass(sub));
+ for (int i = 0; i < 5; i++) {
+ testprintf("objc_msgLookup\n");
+ state = 0;
+ idmsg = (typeof(idmsg))objc_msgLookup;
+ idval = nil;
+ (*idmsg)(sub, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ CALL_LOOKUP(idval);
+ testassert(state == 101);
+ testassert(idval == ID_RESULT);
+
+ testprintf("objc_msgLookup_stret\n");
+ state = 0;
+ stretmsg = (typeof(stretmsg))objc_msgLookup_stret;
+ stretval = zero;
+ (*stretmsg)(sub, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ CALL_LOOKUP_STRET(stretval);
+ testassert(state == 103);
+ testassert(stret_equal(stretval, STRET_RESULT));
+
+ testprintf("objc_msgLookupSuper2\n");
+ state = 100;
+ sup_st.receiver = sub;
+ sup_st.super_class = object_getClass(sub);
+ idmsgsuper = (typeof(idmsgsuper))objc_msgLookupSuper2;
+ idval = nil;
+ idval = (*idmsgsuper)(&sup_st, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ CALL_LOOKUP(idval);
+ testassert(state == 1);
+ testassert(idval == ID_RESULT);
+
+ testprintf("objc_msgLookupSuper2_stret\n");
+ state = 100;
+ sup_st.receiver = sub;
+ sup_st.super_class = object_getClass(sub);
+ stretmsgsuper = (typeof(stretmsgsuper))objc_msgLookupSuper2_stret;
+ stretval = zero;
+ (*stretmsgsuper)(&sup_st, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ CALL_LOOKUP_STRET(stretval);
+ testassert(state == 3);
+ testassert(stret_equal(stretval, STRET_RESULT));
+
+#if __i386__
+ // fixme fpret, can't test FP stack properly
+#endif
+#if __x86_64__
+ // fixme fpret, can't test FP stack properly
+ // fixme fp2ret, can't test FP stack properly
+#endif
+
+ }
+
+ // msgLookup to nil
+ // msgLookup to nil stret
+ // fixme msgLookup to nil long long
+ // fixme msgLookup to nil fpret
+ // fixme msgLookup to nil fp2ret
+
+ testprintf("objc_msgLookup to nil\n");
+ state = 0;
+ idmsg = (typeof(idmsg))objc_msgLookup;
+ idval = nil;
+ (*idmsg)(NIL_RECEIVER, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ CALL_LOOKUP(idval);
+ testassert(state == 0);
+ testassert(idval == nil);
+
+ testprintf("objc_msgLookup_stret to nil\n");
+ state = 0;
+ stretmsg = (typeof(stretmsg))objc_msgLookup_stret;
+ stretval = zero;
+ (*stretmsg)(NIL_RECEIVER, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ CALL_LOOKUP_STRET(stretval);
+ testassert(state == 0);
+ // no stret result guarantee
+
+#if __i386__
+ // fixme fpret, can't test FP stack properly
+#endif
+#if __x86_64__
+ // fixme fpret, can't test FP stack properly
+ // fixme fp2ret, can't test FP stack properly
+#endif
+
+ // objc_msgLookup
+#endif
+
+
+
+#if !TEST_DWARF
+ testwarn("no unwind tables in this configuration " NO_DWARF_REASON);
+#else
+ // DWARF unwind tables
+ testprintf("unwind tables\n");
+
+ // Clear simulator-related environment variables.
+ // Disassembly will run llvm-objdump which is not a simulator executable.
+ unsetenv("DYLD_ROOT_PATH");
+ unsetenv("DYLD_FALLBACK_LIBRARY_PATH");
+ unsetenv("DYLD_FALLBACK_FRAMEWORK_PATH");
+
+ // Check mprotect() of objc_msgSend.
+ // It doesn't work when running on a device with no libobjc root.
+ // In that case we skip this part of the test without failing.
+ // fixme make this work
+ // fixme now it doesn't work even with a libobjc root in place?
+ int err1 = mprotect((void *)((uintptr_t)&objc_msgSend & ~(PAGE_MAX_SIZE-1)),
+ PAGE_MAX_SIZE, PROT_READ | PROT_WRITE);
+ int errno1 = errno;
+ int err2 = mprotect((void *)((uintptr_t)&objc_msgSend & ~(PAGE_MAX_SIZE-1)),
+ PAGE_MAX_SIZE, PROT_READ | PROT_EXEC);
+ int errno2 = errno;
+ if (err1 || err2) {
+ testwarn("can't mprotect() objc_msgSend (%d, %d). "
+ "Skipping unwind table test.",
+ err1, errno1, err2, errno2);
+ }
+ else {
+ // install exception handler
+ struct sigaction act;
+ act.sa_sigaction = break_handler;
+ act.sa_mask = 0;
+ act.sa_flags = SA_SIGINFO;
+ sigaction(BREAK_SIGNAL, &act, nil);
+#if defined(BREAK_SIGNAL2)
+ sigaction(BREAK_SIGNAL2, &act, nil);
+#endif
+
+ SubDW *dw = [[SubDW alloc] init];
+
+ objc_setForwardHandler((void*)test_dw_forward, (void*)test_dw_forward_stret);
+
+# if __x86_64__
+ test_dw("objc_msgSend", dw, tagged, exttagged, false, 0);
+ test_dw("objc_msgSend_stret", dw, tagged, exttagged, true, 0);
+ test_dw("objc_msgSend_fpret", dw, tagged, exttagged, false, 0);
+ test_dw("objc_msgSend_fp2ret", dw, tagged, exttagged, false, 0);
+ test_dw("objc_msgSendSuper", dw, tagged, exttagged, false, 0);
+ test_dw("objc_msgSendSuper2", dw, tagged, exttagged, false, 0);
+ test_dw("objc_msgSendSuper_stret", dw, tagged, exttagged, true, 0);
+ test_dw("objc_msgSendSuper2_stret", dw, tagged, exttagged, true, 0);
+# elif __i386__
+ test_dw("objc_msgSend", dw, dw, dw, false, 0);
+ test_dw("objc_msgSend_stret", dw, dw, dw, true, 0);
+ test_dw("objc_msgSend_fpret", dw, dw, dw, false, 0);
+ test_dw("objc_msgSendSuper", dw, dw, dw, false, 0);
+ test_dw("objc_msgSendSuper2", dw, dw, dw, false, 0);
+ test_dw("objc_msgSendSuper_stret", dw, dw, dw, true, 0);
+ test_dw("objc_msgSendSuper2_stret", dw, dw, dw, true, 0);
+# elif __arm64__
+ test_dw("objc_msgSend", dw, tagged, exttagged, false, 1);
+ test_dw("objc_msgSendSuper", dw, tagged, exttagged, false, 1);
+ test_dw("objc_msgSendSuper2", dw, tagged, exttagged, false, 1);
+# elif __arm__
+ test_dw("objc_msgSend", dw, dw, dw, false, 0);
+ test_dw("objc_msgSend_stret", dw, dw, dw, true, 0);
+ test_dw("objc_msgSendSuper", dw, dw, dw, false, 0);
+ test_dw("objc_msgSendSuper2", dw, dw, dw, false, 0);
+ test_dw("objc_msgSendSuper_stret", dw, dw, dw, true, 0);
+ test_dw("objc_msgSendSuper2_stret", dw, dw, dw, true, 0);
+# else
+# error unknown architecture
+# endif
+ }
+
+ // end DWARF unwind test
+#endif
+
+ } POP_POOL;
+ succeed(__FILE__);
+}