/*
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, "\n mov $0, "\n mov $0, "\n mov $0, "\n mov $0, "\n mov $0, "\n mov $0, "\n mov $0, "\n mov $0, "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n ret");
#elif __i386__
asm("\n .text"
"\n .globl _stomp"
"\n _stomp:"
"\n mov $0, "\n mov $0, "\n mov $0, "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\n xorps "\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
stomp();
char *realName;
asprintf(&realName, "IMPL_ 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 ("\n movq "\n movq "\n addq $8, _rsp(// save other non-volatile registers to variables
"\n movq "\n movq "\n movq "\n movq "\n movq "\n movq "\n jmpq * );
__asm__(
"\n .text"
"\n .globl _callit_stret"
"\n _callit_stret:"
// save sp and return address to variables
"\n movq ("\n movq "\n movq "\n addq $8, _rsp(// save other non-volatile registers to variables
"\n movq "\n movq "\n movq "\n movq "\n movq "\n movq "\n jmpq * );
// 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 "\n movl ("\n movl "\n movl "\n addl $4, _esp-1b("\n movl $0, _espfix-1b(// save other non-volatile registers to variables
"\n movl "\n movl "\n movl "\n movl "\n jmpl *12( );
__asm__(
"\n .text"
"\n .globl _callit_stret"
"\n _callit_stret:"
// save sp and return address to variables
"\n call 1f"
"\n 1: popl "\n movl ("\n movl "\n movl "\n addl $4, _esp-1b("\n movl $4, _espfix-1b(// save other non-volatile registers to variables
"\n movl "\n movl "\n movl "\n movl "\n jmpl *16( );
// 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( 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( 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 // 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 ( }
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 }
struct stat st;
if (fstat(placeholder, &st) < 0) {
fail("couldn't stat asm-placeholder.exe ( }
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 ( }
if (pwrite(fd, buf, st.st_size, 0) != st.st_size) {
fail("couldn't write asm temp file }
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 }
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, " FILE *disa = popen(cmd, "r");
if (!disa) {
fail("couldn't popen }
free(cmd);
free(tempname);
// read past "_main:" line
char *line;
size_t len;
while ((line = fgetln(disa, &len))) {
testprintf("ASM: 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, " if (base == 0) base = addr;
testprintf("ASM: // 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 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
// 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] if (sels[align][0]) {
sels[align][1] = lotsOfSels[i];
} else {
sels[align][0] = lotsOfSels[i];
}
}
}
if (!sels[align][0]) fail("no SEL with alignment if (!sels[align][1]) fail("only one SEL with alignment }
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
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
// 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) 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) 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 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 name, insnOffsets[oo]);
}
}
fail("wrong instructions not reached for 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 # define CALL_LOOKUP_STRET(ret) \
asm volatile ("mov x8,
# elif __arm__
# define CALL_LOOKUP(ret) \
asm volatile ("blx r12 \n mov # define CALL_LOOKUP_STRET(ret) \
asm volatile ("mov r0,
# elif __x86_64__
# define CALL_LOOKUP(ret) \
asm volatile ("call *# define CALL_LOOKUP_STRET(ret) \
asm volatile ("mov
# elif __i386__
# define CALL_LOOKUP(ret) \
asm volatile ("call *# define CALL_LOOKUP_STRET(ret) \
asm volatile ("add $4,
# 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 ( "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__);
}