// TEST_CFLAGS -Wno-unused-parameter -Wundeclared-selector
#include "test.h"
#include "testroot.i"
#if __cplusplus && !__clang__
int main()
{
// llvm-g++ is confused by @selector(foo::) and will never be fixed
succeed(__FILE__);
}
#else
#include <objc/objc.h>
#include <objc/runtime.h>
#include <objc/objc-internal.h>
#include <objc/objc-abi.h>
#include <simd/simd.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 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(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)); \
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); \
} 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 q4, #0"
"\n vmov.i32 q5, #0"
"\n vmov.i32 q6, #0"
"\n vmov.i32 q7, #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;
}
-(void)voidret_nop
{
return;
}
-(void)voidret_nop2
{
return;
}
-(id)idret_nop
{
return ID_RESULT;
}
-(long long)llret_nop
{
return LL_RESULT;
}
-(struct stret)stret_nop
{
return STRET_RESULT;
}
-(double)fpret_nop
{
return FP_RESULT;
}
-(long double)lfpret_nop
{
return LFP_RESULT;
}
-(vector_ulong2)vecret_nop
{
return VEC_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_7, self);
}
@end
#endif
// DWARF checking machinery
#if TARGET_OS_WIN32
// unimplemented on this platform
#elif !__OBJC2__
// 32-bit Mac doesn't use DWARF unwind
#elif TARGET_OS_IPHONE && __arm__
// 32-bit iOS device doesn't use DWARF unwind
#elif __has_feature(objc_arc)
// ARC's extra RR calls hit the traps at the wrong times
#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>
#define UNW_STEP_SUCCESS 1
#define UNW_STEP_END 0
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__
#define OTOOL "/usr/bin/xcrun otool -arch x86_64 "
typedef uint8_t insn_t;
#define BREAK_INSN ((insn_t)0xcc) // int3
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 == UNW_STEP_SUCCESS);
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 sigtrap(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 == SIGTRAP);
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 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__
#define OTOOL "/usr/bin/xcrun otool -arch i386 "
typedef uint8_t insn_t;
#define BREAK_INSN ((insn_t)0xcc) // int3
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 == UNW_STEP_SUCCESS);
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 sigtrap(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 == SIGTRAP);
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>
// runs on iOS device, no xcrun command present
#define OTOOL "/usr/bin/otool -arch arm64 "
typedef uint32_t insn_t;
#define BREAK_INSN ((insn_t)0xd4200020) // brk #1
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;
err = unw_init_local(&curs, (unw_context_t *)state);
testassert(!err);
step = unw_step(&curs);
testassert(step == UNW_STEP_SUCCESS);
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);
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 thread state to unwound state
state->__x[19] = x19;
state->__x[20] = x20;
state->__x[20] = 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->__fp = fp;
state->__lr = pc; // libunwind restores PC into LR
state->__sp = sp;
state->__pc = pc;
caught = true;
}
void sigtrap(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 == SIGTRAP);
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
#else
#error unknown architecture
#endif
insn_t set(uintptr_t dst, insn_t newvalue)
{
uintptr_t start = dst & ~(PAGE_MAX_SIZE-1);
mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_WRITE);
insn_t oldvalue = *(insn_t *)dst;
*(insn_t *)dst = newvalue;
mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_EXEC);
return oldvalue;
}
insn_t clobber(void *fn, uintptr_t offset)
{
clobbered = (uintptr_t)fn + offset;
return set((uintptr_t)fn + offset, BREAK_INSN);
}
void unclobber(void *fn, uintptr_t offset, insn_t oldvalue)
{
set((uintptr_t)fn + offset, oldvalue);
}
uintptr_t *getOffsets(void *symbol, const char *symname, uintptr_t *outBase)
{
uintptr_t *result = (uintptr_t *)malloc(1000 * sizeof(uintptr_t));
uintptr_t *end = result + 1000;
uintptr_t *p = result;
// find library
Dl_info dl;
dladdr(symbol, &dl);
// call `otool` on library
unsetenv("DYLD_LIBRARY_PATH");
unsetenv("DYLD_ROOT_PATH");
unsetenv("DYLD_INSERT_LIBRARIES");
unsetenv("DYLD_SHARED_REGION");
unsetenv("DYLD_SHARED_CACHE_DIR");
unsetenv("DYLD_SHARED_CACHE_DONT_VALIDATE");
char *cmd;
asprintf(&cmd, OTOOL "-tv -p _ symname, dl.dli_fname);
testprintf(" FILE *disa = popen(cmd, "r");
free(cmd);
testassert(disa);
// read past "_symname:" line
char *line;
size_t len;
while ((line = fgetln(disa, &len))) {
if (0 == strncmp(1+line, symname, MIN(len-1, strlen(symname)))) break;
}
// read instructions and save offsets
char op[128];
long base = 0;
long addr;
while (2 == fscanf(disa, " if (base == 0) base = addr;
if (0 != strncmp(op, "nop", 3)) {
testassert(p < end);
*p++ = addr - base;
} else {
// assume nops are unreached (e.g. alignment padding)
}
}
pclose(disa);
#if __arm64__
// Also add breakpoints in _objc_msgSend_uncached_impcache
// (which is the slow path and has a frame to unwind)
if (0 != strcmp(symname, "_objc_msgSend_uncached_impcache")) {
uintptr_t base2;
uintptr_t *more_offsets = getOffsets(symbol, "_objc_msgSend_uncached_impcache", &base2);
uintptr_t *q = more_offsets;
// Skip prologue because it's imprecisely modeled in compact unwind
testassert(*q != ~0UL);
q++;
testassert(*q != ~0UL);
q++;
while (*q != ~0UL) *p++ = *q++ + base2 - base;
// Skip return because it's imprecisely modeled in compact unwind
p--;
free(more_offsets);
}
#endif
testassert(p > result);
testassert(p < end);
*p = ~0UL;
#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
p[-1] = ~0UL;
#endif
if (outBase) *outBase = base;
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, 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_replaceMethod(cls, sel, imp, "");
class_replaceMethod(tagcls, 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, "");
}
}
#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);
testassert(fn);
// argument substitutions
void *sub_arg = (void*)objc_unretainedPointer(sub);
void *tagged_arg = (void*)objc_unretainedPointer(tagged);
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 { void *imp; SEL sel; } message_ref = { fn, sel };
Class cache_cls = object_getClass(sub);
if (strstr(name, "Super")) {
// super version - replace receiver with objc_super
// clear caches of superclass
cache_cls = class_getSuperclass(cache_cls);
sub_arg = &sup_st;
tagged_arg = &tagged_sup_st;
}
if (strstr(name, "_fixup")) {
// fixup version - replace sel with message_ref
sel_arg = &message_ref;
}
uintptr_t *insnOffsets = getOffsets(fn, name, nil);
uintptr_t offset;
int uncaughtCount = 0;
for (int oo = 0; insnOffsets[oo] != ~0UL; oo++) {
offset = insnOffsets[oo];
testprintf("OFFSET
insn_t saved_insn = clobber(fn, offset);
caught = false;
// nil
if ((void*)objc_unretainedPointer(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(cache_cls);
CALLIT(tagged_arg, sel_arg, sel, fn, stret);
_objc_flush_caches(cache_cls);
CALLIT(tagged_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);
// multiple SEL alignments, collisions, wraps
SELF = sub;
for (int a = 0; a < ALIGNCOUNT; a++) {
testprintf(" cached, 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;
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));
#if __OBJC2__
// 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
#endif
}
testprintf("basic done\n");
}
int main()
{
PUSH_POOL {
int i;
id idval;
long long llval;
struct stret stretval;
double fpval;
long double lfpval;
vector_ulong2 vecval;
#if __x86_64__
struct stret *stretptr;
#endif
uint64_t startTime;
uint64_t totalTime;
uint64_t targetTime;
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];
#endif
ID_RESULT = [Super new];
Sub *sub = [Sub new];
Super *sup = [Super new];
#if OBJC_HAVE_TAGGED_POINTERS
TaggedSub *tagged = objc_unretainedObject(_objc_makeTaggedPointer(OBJC_TAG_7, 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);
#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;
// cached message performance
// catches failure to cache or (abi=2) failure to fixup (#5584187)
// fixme unless they all fail
// `.align 4` matches loop alignment to make -O0 work
// fill cache first
testprintf("time checks\n");
SELF = sub;
[sub voidret_nop];
[sub voidret_nop2];
[sub llret_nop];
[sub stret_nop];
[sub fpret_nop];
[sub lfpret_nop];
[sub vecret_nop];
[sub voidret_nop];
[sub voidret_nop2];
[sub llret_nop];
[sub stret_nop];
[sub fpret_nop];
[sub lfpret_nop];
[sub vecret_nop];
[sub voidret_nop];
[sub voidret_nop2];
[sub llret_nop];
[sub stret_nop];
[sub fpret_nop];
[sub lfpret_nop];
[sub vecret_nop];
// Some of these times have high variance on some compilers.
// The errors we're trying to catch should be catastrophically slow,
// so the margins here are generous to avoid false failures.
// Use voidret because id return is too slow for perf test with ARC.
// Pick smallest of voidret_nop and voidret_nop2 time
// in the hopes that one of them didn't collide in the method cache.
#define COUNT 1000000
startTime = mach_absolute_time();
ALIGN_();
for (i = 0; i < COUNT; i++) {
[sub voidret_nop];
}
totalTime = mach_absolute_time() - startTime;
testprintf("time: voidret targetTime = totalTime;
startTime = mach_absolute_time();
ALIGN_();
for (i = 0; i < COUNT; i++) {
[sub voidret_nop2];
}
totalTime = mach_absolute_time() - startTime;
testprintf("time: voidret2 if (totalTime < targetTime) targetTime = totalTime;
startTime = mach_absolute_time();
ALIGN_();
for (i = 0; i < COUNT; i++) {
[sub llret_nop];
}
totalTime = mach_absolute_time() - startTime;
timecheck("llret ", totalTime, targetTime * 0.7, targetTime * 2.0);
startTime = mach_absolute_time();
ALIGN_();
for (i = 0; i < COUNT; i++) {
[sub stret_nop];
}
totalTime = mach_absolute_time() - startTime;
timecheck("stret ", totalTime, targetTime * 0.7, targetTime * 5.0);
startTime = mach_absolute_time();
ALIGN_();
for (i = 0; i < COUNT; i++) {
[sub fpret_nop];
}
totalTime = mach_absolute_time() - startTime;
timecheck("fpret ", totalTime, targetTime * 0.7, targetTime * 4.0);
startTime = mach_absolute_time();
ALIGN_();
for (i = 0; i < COUNT; i++) {
[sub lfpret_nop];
}
totalTime = mach_absolute_time() - startTime;
timecheck("lfpret", totalTime, targetTime * 0.7, targetTime * 4.0);
startTime = mach_absolute_time();
ALIGN_();
for (i = 0; i < COUNT; i++) {
[sub vecret_nop];
}
totalTime = mach_absolute_time() - startTime;
timecheck("vecret", totalTime, targetTime * 0.7, targetTime * 4.0);
#if __arm64__
// Removing this testwarn(), or changing voidret_nop to nop;ret,
// changes the voidret_nop and stret_nop times above by a factor of 2.
testwarn("rdar://13896922 nop;ret is faster than ret?");
#endif
#undef COUNT
// 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);
#if __clang__
testassert(0 == memcmp(&stretval, &zero, sizeof(stretval)));
#else
// no stret result guarantee
#endif
#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, even with clang
#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);
#if __OBJC2__
// 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
#endif
#if __OBJC2__
// 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));
#endif
#if __OBJC2__ && !__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
#if !TEST_DWARF
testwarn("no unwind tables in this configuration");
#else
// DWARF unwind tables
testprintf("unwind tables\n");
// install exception handler
struct sigaction act;
act.sa_sigaction = sigtrap;
act.sa_mask = 0;
act.sa_flags = SA_SIGINFO;
sigaction(SIGTRAP, &act, NULL);
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, false, 0);
test_dw("objc_msgSend_stret", dw, tagged, true, 0);
test_dw("objc_msgSend_fpret", dw, tagged, false, 0);
test_dw("objc_msgSend_fp2ret", dw, tagged, false, 0);
test_dw("objc_msgSendSuper", dw, tagged, false, 0);
test_dw("objc_msgSendSuper2", dw, tagged, false, 0);
test_dw("objc_msgSendSuper_stret", dw, tagged, true, 0);
test_dw("objc_msgSendSuper2_stret", dw, tagged, true, 0);
# elif __i386__
test_dw("objc_msgSend", dw, dw, false, 0);
test_dw("objc_msgSend_stret", dw, dw, true, 0);
test_dw("objc_msgSend_fpret", dw, dw, false, 0);
test_dw("objc_msgSendSuper", dw, dw, false, 0);
test_dw("objc_msgSendSuper2", dw, dw, false, 0);
test_dw("objc_msgSendSuper_stret", dw, dw, true, 0);
test_dw("objc_msgSendSuper2_stret", dw, dw, true, 0);
# elif __arm64__
test_dw("objc_msgSend", dw, tagged, false, 1);
test_dw("objc_msgSendSuper", dw, tagged, false, 1);
test_dw("objc_msgSendSuper2", dw, tagged, false, 1);
# else
# error unknown architecture
# endif
// DWARF unwind tables
#endif
} POP_POOL;
succeed(__FILE__);
}
#endif