ObjC runtime work: objc_msgSend and objc_msgSend_stret, ivars support, working class reg (w/o protocols), trivial benchmarking framework

This commit is contained in:
Lubos Dolezel 2012-10-03 23:37:58 +02:00
parent db88d34643
commit 758e751594
20 changed files with 589 additions and 312 deletions

92
benchmarks/runtest Executable file
View File

@ -0,0 +1,92 @@
#!/bin/bash
BUILDSERVER="osx"
CPPFLAGS=""
DYLD="dyld"
set -e
failure() {
tput setaf 1
tput bold
echo "Benchmark failure - the last command failed to execute"
tput sgr0
}
runtest() {
trap failure ERR
source="$1"
extension="${source##*.}"
source_fn=$(echo "$source" | sed 's/\//_/g')
cflags="$(grep '// CFLAGS' "$source" || true)"
cflags="-O3 $CPPFLAGS -w $(echo "$cflags" | cut -b 12-)"
case "$extension" in
"cpp")
darwin_tool="g++"
native_tool="clang++"
;;
"c")
darwin_tool="gcc"
native_tool="clang"
;;
"m")
darwin_tool="gcc"
native_tool="clang"
cflags_native="-l:libobjc.so.4 -lgnustep-base"
cflags_darwin="-lobjc"
;;
*)
echo "Unsupported file type: $extension"
exit 1
esac
tput bold
echo "====="
echo "Running benchmark '$source'"
echo "====="
tput sgr0
cflags="$(grep '// CFLAGS' "$source" || true)"
cflags="$CPPFLAGS -w $(echo "$cflags" | cut -b 12-)"
echo "Copying the source code to Darwin..."
scp "$source" "$BUILDSERVER:/tmp/$$.$source_fn" >/dev/null
echo "Building the source code for Darwin..."
ssh "$BUILDSERVER" "$darwin_tool $cflags $cflags_darwin '/tmp/$$.$source_fn' -o '/tmp/$$.$source_fn.bin'"
echo "Copying the binary over..."
scp "$BUILDSERVER:/tmp/$$.$source_fn.bin" "/tmp" >/dev/null
ssh "$BUILDSERVER" "rm -f /tmp/$$.$source_fn*"
echo "Running Darwin binary..."
tput bold
time $DYLD "/tmp/$$.$source_fn.bin"
tput sgr0
rm -f "/tmp/$$.$source_fn.bin"
echo "Compiling native..."
$native_tool $cflags $cflags_native "$source" -o "/tmp/$$.$source_fn.bin"
echo "Running native binary..."
tput bold
time "/tmp/$$.$source_fn.bin"
tput sgr0
rm -f "/tmp/$$.$source_fn.bin"
}
if [[ "$0" == *runtest32 ]]; then
CPPFLAGS="-m32"
DYLD="dyld32"
echo "32-bit mode"
fi
if [[ "$0" == *runtest64 ]]; then
DYLD="dyld64"
echo "64-bit mode"
fi
for test in "$@"; do
runtest "$test"
done

1
benchmarks/runtest32 Symbolic link
View File

@ -0,0 +1 @@
runtest

1
benchmarks/runtest64 Symbolic link
View File

@ -0,0 +1 @@
runtest

View File

@ -0,0 +1,29 @@
#include <stdio.h>
#import <Foundation/NSObject.h>
@interface helloclass : NSObject {
}
+ (int)mult:(int)a :(int)b;
@end
@implementation helloclass
+ (int)mult:(int)a :(int)b
{
return a*b;
}
@end
int main()
{
int i;
volatile int j;
for (i = 0; i < 10000000; i++)
{
j += [helloclass mult:1:2];
}
return 0;
}

View File

@ -0,0 +1,37 @@
#include <stdio.h>
#import <Foundation/NSObject.h>
struct st
{
float f1;
int buf[50];
};
@interface helloclass : NSObject {
}
+ (struct st)mult:(float)a :(float)b;
@end
@implementation helloclass
+ (struct st)mult:(float)a :(float)b
{
struct st s;
s.f1 = a+b;
return s;
}
@end
int main()
{
int i;
for (i = 0; i < 10000000; i++)
{
struct st v = [helloclass mult:1.0f:2.0f];
v.f1;
}
return 0;
}

View File

@ -14,12 +14,15 @@ enable_language(ASM_NASM)
#configure_file(config.h.in config.h)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -fPIC -ggdb")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -fPIC -ggdb")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fPIC")
set(objcdarwin_SRCS
objc_msgSend.nasm
objc_msgSend_fixup.nasm
objc_msgSend_stret.nasm
registers.nasm
NameTranslate.cpp
ClassRegister.cpp
ClassRegister.mm
)
add_library(objc.A.dylib SHARED ${objcdarwin_SRCS})

View File

@ -1,214 +0,0 @@
#include "ClassRegister.h"
#include "../dyld/public.h"
#include "class.h"
#include "method_list.h"
#include <iostream>
#include <map>
#include <cstring>
#include <cassert>
/*
__DATA,__objc_classlist was __OBJC2,__class_list
__DATA,__objc_catlist was __OBJC2,__category_list
__DATA,__objc_protolist was __OBJC2,__protocol_list
__DATA,__objc_msgrefs was __OBJC2,__message_refs
__DATA,__objc_classrefs was __OBJC2,__class_refs
__DATA,__objc_superrefs was __OBJC2,__super_refs
__DATA,__objc_imageinfo was __OBJC,__image_info
*/
#define SEG_OBJC_CLASSLIST_NEW "__DATA"
#define SECT_OBJC_CLASSLIST_NEW "__objc_classlist"
#define SEG_OBJC_CLASSLIST_OLD "__OBJC"
#define SECT_OBJC_CLASSLIST_OLD "__class" // TODO: check if correct
static std::map<const void*, objc_class*> g_convertedClasses;
static std::pair<uintptr_t, uintptr_t> g_cstringSection;
static void ProcessImageLoad(const struct mach_header* mh, intptr_t slide);
static void ProcessImageUnload(const struct mach_header* mh, intptr_t slide);
static void RegisterClass(const class_t* cls);
static void RegisterClass(const old_class* cls);
static old_class* PtrToClass(old_class_ptr ptr);
// Here we process Mach-O files that have been loaded before this native library
// Then we register a handler to process all images loaded in the future
__attribute__((constructor))
void RegisterAlreadyLoadedClasses()
{
for (uint32_t i = 0; i < _dyld_image_count(); i++)
{
const struct mach_header* hdr = _dyld_get_image_header(i);
ProcessImageLoad(hdr, 0);
}
_dyld_register_func_for_add_image(ProcessImageLoad);
_dyld_register_func_for_remove_image(ProcessImageUnload);
//std::cout << "Done registering\n";
}
Class objcdarwin_class_lookup(const class_t* cls)
{
//std::cout << "Looking up " << cls << std::endl << std::flush;
auto it = g_convertedClasses.find(cls);
if (it != g_convertedClasses.end())
return it->second;
else
return Class(cls); // assume native or instance ptr
}
// This is very wrong. Does it have to be this way?s
old_class* PtrToClass(old_class_ptr ptr)
{
if (ptr.ptrValue >= g_cstringSection.first && ptr.ptrValue < g_cstringSection.first+g_cstringSection.second)
{
//std::cout << ptr.cls << " found to be a string: " << ptr.name << std::endl;
id cls = objc_getClass(ptr.name); // it is a string
assert(cls != nullptr);
return (old_class*) cls;
}
else
{
//std::cout << ptr.cls << " found to be ptr to class.\n";
return ptr.cls;
}
}
template<typename ListType> void ConvertMethodListGen(Class c, const ListType* list)
{
std::cout << list->count << " methods within\n";
for (size_t i = 0; i < list->count; i++)
{
auto* m = &list->method_list[i];
std::cout << "Method: selName: " << m->selName << "; types: " << m->types << "; impl: " << m->impl << std::endl;
SEL sel = sel_registerName(m->selName);
class_addMethod(c, sel, reinterpret_cast<IMP>(m->impl), m->types);
}
}
void RegisterClass(const class_t* cls)
{
std::cout << "Processing ObjC class " << cls->data()->className;
std::cout << std::endl;
// objc_allocateClassPair
// objc_registerClassPair
const class_t* meta = cls->isa;
Class conv, super;
if (g_convertedClasses.find(cls->superclass) != g_convertedClasses.end())
super = g_convertedClasses[cls->superclass];
else
super = reinterpret_cast<objc_class*>(cls->superclass);
conv = objc_allocateClassPair(super, cls->data()->className, 0);
const class_ro_t* ro = cls->data();
const class_ro_t* roMeta = meta->data();
if (ro->baseMethods)
ConvertMethodListGen(conv, ro->baseMethods);
if (roMeta->baseMethods)
ConvertMethodListGen(conv->isa, roMeta->baseMethods);
conv->instance_size = ro->instSize;
conv->isa->instance_size = roMeta->instSize;
objc_registerClassPair(conv);
g_convertedClasses[cls] = conv;
g_convertedClasses[meta] = conv->isa;
}
void RegisterClass(const old_class* cls)
{
std::cout << "Processing old ObjC class " << cls->name;
std::cout << std::endl;
const old_class* meta = PtrToClass(cls->isa);
Class conv, super;
old_class* psuper = PtrToClass(cls->super_class);
if (g_convertedClasses.find(psuper) != g_convertedClasses.end())
super = g_convertedClasses[psuper];
else
super = reinterpret_cast<objc_class*>(psuper);
conv = objc_allocateClassPair(super, cls->name, 0);
std::cout << "Instance methods...\n";
if (cls->methodList)
ConvertMethodListGen(conv, cls->methodList);
std::cout << "Class methods...\n";
if (meta->methodList)
ConvertMethodListGen(conv->isa, meta->methodList);
objc_registerClassPair(conv);
assert(id(conv) == objc_getClass(cls->name));
assert(id(conv->isa) == objc_getMetaClass(cls->name));
assert(class_isMetaClass(conv->isa));
assert(!class_isMetaClass(conv));
g_convertedClasses[cls] = conv;
g_convertedClasses[cls->name] = conv;
g_convertedClasses[meta] = conv->isa;
/*
SEL ss = sel_get_any_uid("doHello:::::::");
std::cout << "objc_msg_lookup: " << conv << ' ' << ss << std::endl;
void* p = (void*) objc_msg_lookup(id(conv), ss);
std::cout << "Impl: " << p << "; Sel: " << ss << std::endl;
*/
}
void ProcessImageLoad(const struct mach_header* mh, intptr_t)
{
unsigned long size;
const class_t** classes;
classes = reinterpret_cast<const class_t**>(
getsectdata(mh, SEG_OBJC_CLASSLIST_NEW, SECT_OBJC_CLASSLIST_NEW, &size)
);
if (!classes)
{
// Try the old runtime
const old_class* classes;
classes = reinterpret_cast<const old_class*>(
getsectdata(mh, SEG_OBJC_CLASSLIST_OLD, SECT_OBJC_CLASSLIST_OLD, &size)
);
if (classes)
{
// g_cstringSection
unsigned long cstrLen;
void* ptr;
ptr = getsectdata(mh, "__TEXT", "__cstring", &cstrLen);
if (ptr)
g_cstringSection = std::pair<uintptr_t,uintptr_t>(uintptr_t(ptr), cstrLen);
else
g_cstringSection = std::pair<uintptr_t,uintptr_t>(0, 0);
for (size_t i = 0; i < size / sizeof(old_class); i++)
RegisterClass(classes+i);
}
}
else
{
for (size_t i = 0; i < size / sizeof(class_t*); i++)
RegisterClass(classes[i]);
}
}
void ProcessImageUnload(const struct mach_header* mh, intptr_t)
{
// TODO
}

View File

@ -1,9 +1,14 @@
#ifndef CLASSREGISTER_H
#define CLASSREGISTER_H
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <cstddef>
#include <objc/runtime.h>
#ifndef INTPTR_MAX
#error
#endif
struct method_t
{
const char* selName;
@ -20,6 +25,20 @@ struct method_list_t
uint32_t entsize() const { return entsize_and_flags & ~uint32_t(3); }
};
struct ivar_t
{
uintptr_t* offset;
const char* name;
const char* type;
uint32_t alignment, size;
};
struct ivar_list_t
{
uint32_t entsize, count;
ivar_t ivar_list[];
};
struct class_ro_t
{
uint32_t flags, instStart, instSize;
@ -30,6 +49,7 @@ struct class_ro_t
const char* className;
const method_list_t* baseMethods; // instance methods for classes, static methods for metaclasses
const void* baseProtocols;
const ivar_list_t* ivars;
void* todo[2]; // TODO: two more pointers
};
@ -108,9 +128,9 @@ struct old_category
struct old_ivar
{
char *ivar_name;
char *ivar_type;
int ivar_offset;
char *name;
char *type;
int offset;
#ifdef __x86_64__
int space;
#endif
@ -118,7 +138,7 @@ struct old_ivar
struct old_ivar_list
{
int ivar_count;
int count;
#ifdef __x86_64__
int space;
#endif

View File

@ -0,0 +1,46 @@
#include <cstring>
#include <memory>
#include <dlfcn.h>
extern "C" void* _objc_empty_cache = 0;
extern "C" void* _objc_empty_vtable = 0;
namespace Darling
{
typedef bool (*DlsymHookFunc)(char* symName);
void registerDlsymHook(DlsymHookFunc func);
void deregisterDlsymHook(DlsymHookFunc func);
};
static bool ClassTranslator(char* symName);
__attribute__((constructor))
static void initTranslation()
{
Darling::registerDlsymHook(ClassTranslator);
}
__attribute__((destructor))
static void exitTranslation()
{
Darling::deregisterDlsymHook(ClassTranslator);
}
static bool ClassTranslator(char* name)
{
if (char* off = strstr(name, "_$_"))
{
size_t offset = off - name;
memmove(name+1, name, strlen(name)+1);
*name = '_'; // prefix with an underscore
offset++;
// remove $_
memmove(name + offset, name + offset + 2, strlen(name + offset + 2)+1);
return true;
}
return false;
}

View File

@ -3,35 +3,14 @@ global __darwin_objc_msgSend
extern objcdarwin_class_lookup
extern objc_msg_lookup
extern sel_get_any_uid
extern objcdarwin_SaveRegisters
extern objcdarwin_RestoreRegisters
%ifidn __OUTPUT_FORMAT__, elf64
BITS 64
section text
SaveRegisters:
pop r11
push r9
push r8
push rcx
push rdx
push rsi
push rdi
; save xmms?
jmp r11
RestoreRegisters:
pop r11
pop rdi
pop rsi
pop rdx
pop rcx
pop r8
pop r9
jmp r11
__darwin_objc_msgSend:
; Procedure:
; 1) get the converted GNU class from an Apple class
@ -39,7 +18,7 @@ __darwin_objc_msgSend:
; 3) run objc_msg_lookup
; 4) jump to the pointer returned by objc_msg_lookup
call SaveRegisters
call objcdarwin_SaveRegisters WRT ..plt
call objcdarwin_class_lookup WRT ..plt
mov [rsp], rax ; save the converted value
@ -49,11 +28,12 @@ __darwin_objc_msgSend:
; rax now has the GNU selector
; move rax to the second argument
mov rsi, rax
mov [rsp+8], rax
; restore the first argument
mov rdi, [rsp]
call objc_msg_lookup WRT ..plt
call RestoreRegisters
call objcdarwin_RestoreRegisters WRT ..plt
jmp rax
%elifidn __OUTPUT_FORMAT__, elf
@ -61,8 +41,6 @@ __darwin_objc_msgSend:
BITS 32
section text
;;;;;;;;;;;;;;;;;;;;;;;;;;;; TODO!!!
__darwin_objc_msgSend:
;enter 8

View File

@ -1,58 +1,49 @@
global objc_msgSend_fixup
global __darwin_objc_msgSend_fixup
extern objcdarwin_class_lookup
extern objc_msg_lookup
extern sel_get_any_uid
extern objcdarwin_SaveRegisters
extern objcdarwin_RestoreRegisters
%ifidn __OUTPUT_FORMAT__, elf64
BITS 64
section text
SaveRegisters:
pop r11
push rdi
push rsi
push rdx
push rcx
push r8
push r9
; save xmms?
jmp r11
;__darwin_objc_msgSend_fixed:
; add rsi, 8
; jmp __darwin_objc_msgSend WRT ..plt
RestoreRegisters:
pop r11
pop r9
pop r8
pop rcx
pop rdx
pop rsi
pop rdi
jmp r11
objc_msgSend_fixup:
__darwin_objc_msgSend_fixup:
; Procedure:
; 1) convert Apple selector to GNU
; 2) run objc_msg_lookup
; 3) save the method address into the second argument
; 4) save the right selector into the second argument
; 5) jump to the pointer returned by objc_msg_lookup
; 1) get the converted GNU class from an Apple class
; 2) convert Apple selector to GNU
; 3) run objc_msg_lookup
; 4) jump to the pointer returned by objc_msg_lookup
call objcdarwin_SaveRegisters WRT ..plt
call objcdarwin_class_lookup WRT ..plt
mov [rsp], rax ; save the converted value
call SaveRegisters
; move the second argument into the first argument
mov rdi, rsi
add rdi, 8 ; selector name is the second pointer in struct
mov rdi, [rsp+8]
add rdi, 8 ; the selector itself is the second element of what we receive as SEL
mov rdi, [rdi]
call sel_get_any_uid WRT ..plt
; rax now has the GNU selector
mov rsi, [rsp+8] ; put the second argument back to rsi
mov [rsi+8], rax ; store the GNU selector
add rsi, 8 ; pass the selector itself
; move rax to the second argument
mov rsi, rax
; restore the first argument
mov rdi, [rsp]
call objc_msg_lookup WRT ..plt
; optimize the next call by fixing the function pointer
mov rsi, [rsp+8]
;mov [rsi], rax ; TODO: fixups not working, the target method still isn't getting the selector it expects
call RestoreRegisters
; can we do it like this? don't we still need a proxy?
mov [rsi], rax ; fix the pointer to make the call direct next time
call objcdarwin_RestoreRegisters WRT ..plt
jmp rax
%elifidn __OUTPUT_FORMAT__, elf
@ -60,26 +51,36 @@ objc_msgSend_fixup:
BITS 32
section text
objc_msgSend_fixup:
; swap the first two arguments
mov ecx, [esp+4]
xchg [esp+8], ecx
add ecx, 4 ; move to the second struct element
mov [esp+4], ecx
call sel_get_any_uid WRT ..plt
; eax now has the GNU selector
; swap the arguments back
__darwin_objc_msgSend_fixup:
mov ecx, [esp+4]
mov [ecx], eax ; fix the selector in the original struct
push ecx ; arg for func call
; swap the arguments back
mov ecx, [esp+4]
xchg [esp+8], ecx
mov [esp+4], ecx
call objc_msg_lookup WRT ..plt
call objcdarwin_class_lookup ;WRT ..plt
add esp, 4 ; remove argument
mov [esp+4], eax ; change the class id
mov ecx, [esp+8] ; second argument
add ecx, 4 ; the selector itself is the second element of what we receive as SEL
mov ecx, [ecx]
push ecx
call sel_get_any_uid ;WRT ..plt
add esp, 4
mov [esp+8], eax
push eax ; reuse the sel_get_any_uid retval
mov eax, [esp+8]
push eax ; class id
call objc_msg_lookup ;WRT ..plt
add esp, 8
; optimize the next call by fixing the function pointer
mov ecx, [esp+8]
;mov [ecx], eax ; TODO: fixups not working, the target method still isn't getting the selector it expects
jmp eax

View File

@ -0,0 +1,77 @@
global __darwin_objc_msgSend_stret
extern objcdarwin_class_lookup
extern objc_msg_lookup
extern sel_get_any_uid
extern objcdarwin_SaveRegisters
extern objcdarwin_RestoreRegisters
%ifidn __OUTPUT_FORMAT__, elf64
BITS 64
section text
; Compared to ordinary msgSend, arguments are shifted by one - first arg is the stret
__darwin_objc_msgSend_stret:
; Procedure:
; 1) get the converted GNU class from an Apple class
; 2) convert Apple selector to GNU
; 3) run objc_msg_lookup
; 4) jump to the pointer returned by objc_msg_lookup
call objcdarwin_SaveRegisters WRT ..plt
mov rdi, rsi
call objcdarwin_class_lookup WRT ..plt
mov [rsp+8], rax ; save the converted value
; move the second argument into the first argument
mov rdi, [rsp+16]
call sel_get_any_uid WRT ..plt
; rax now has the GNU selector
; move rax to the second argument
mov rsi, rax
mov [rsp+16], rax
; restore the first argument
mov rdi, [rsp+8]
call objc_msg_lookup WRT ..plt
call objcdarwin_RestoreRegisters WRT ..plt
jmp rax
%elifidn __OUTPUT_FORMAT__, elf
BITS 32
section text
__darwin_objc_msgSend_stret:
mov ecx, [esp+8]
push ecx ; arg for func call
call objcdarwin_class_lookup ;WRT ..plt
add esp, 4 ; remove argument
mov [esp+8], eax ; change the class id
mov ecx, [esp+12] ; second argument
push ecx
call sel_get_any_uid ;WRT ..plt
add esp, 4
mov [esp+12], eax
push eax ; reuse the sel_get_any_uid retval
mov eax, [esp+12]
push eax ; class id
call objc_msg_lookup ;WRT ..plt
add esp, 8
jmp eax
%else
%error "Unsupported platform"
%endif

View File

@ -0,0 +1,51 @@
%ifidn __OUTPUT_FORMAT__, elf64
BITS 64
section text
global objcdarwin_SaveRegisters
global objcdarwin_RestoreRegisters
objcdarwin_SaveRegisters:
pop r11
sub rsp, 128 ; 8*16
movdqu [rsp], xmm0
movdqu [rsp+16], xmm1
movdqu [rsp+32], xmm2
movdqu [rsp+48], xmm3
movdqu [rsp+64], xmm4
movdqu [rsp+80], xmm5
movdqu [rsp+96], xmm6
movdqu [rsp+112], xmm7
push r9
push r8
push rcx
push rdx
push rsi
push rdi
jmp r11
objcdarwin_RestoreRegisters:
pop r11
pop rdi
pop rsi
pop rdx
pop rcx
pop r8
pop r9
movdqu xmm7, [rsp+112]
movdqu xmm6, [rsp+96]
movdqu xmm5, [rsp+80]
movdqu xmm4, [rsp+64]
movdqu xmm3, [rsp+48]
movdqu xmm2, [rsp+32]
movdqu xmm1, [rsp+16]
movdqu xmm0, [rsp]
add rsp, 128
jmp r11
%endif

View File

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
BUILDSERVER="osx"
CPPFLAGS=""
@ -6,26 +6,48 @@ DYLD="dyld"
set -e
failure() {
tput setaf 1
tput bold
echo "Test failure - the last command failed to execute"
tput sgr0
}
runtest() {
trap failure ERR
source="$1"
extension="${source##*.}"
source_fn=$(echo "$source" | sed 's/\//_/g')
cflags="$(grep '// CFLAGS' "$source" || true)"
cflags="$CPPFLAGS -w $(echo "$cflags" | cut -b 12-)"
case "$extension" in
"cpp")
tool="g++"
darwin_tool="g++"
native_tool="clang++"
;;
"c" | "m")
tool="gcc"
"c")
darwin_tool="gcc"
native_tool="clang"
;;
"m")
darwin_tool="gcc"
native_tool="clang"
cflags_native="-l:libobjc.so.4 -lgnustep-base"
cflags_darwin="-lobjc"
;;
*)
echo "Unsupported file type: $extension"
exit 1
esac
tput bold
echo "====="
echo "Running test '$source'"
echo "====="
tput sgr0
cflags="$(grep '// CFLAGS' "$source" || true)"
cflags="$CPPFLAGS -w $(echo "$cflags" | cut -b 12-)"
@ -33,7 +55,7 @@ runtest() {
echo "Copying the source code to Darwin..."
scp "$source" "$BUILDSERVER:/tmp/$$.$source_fn" >/dev/null
echo "Building the source code for Darwin..."
ssh "$BUILDSERVER" "$tool $cflags '/tmp/$$.$source_fn' -o '/tmp/$$.$source_fn.bin'"
ssh "$BUILDSERVER" "$darwin_tool $cflags $cflags_darwin '/tmp/$$.$source_fn' -o '/tmp/$$.$source_fn.bin'"
echo "Copying the binary over..."
scp "$BUILDSERVER:/tmp/$$.$source_fn.bin" "/tmp" >/dev/null
ssh "$BUILDSERVER" "rm -f /tmp/$$.$source_fn*"
@ -43,25 +65,34 @@ runtest() {
rm -f "/tmp/$$.$source_fn.bin"
echo "Compiling native..."
$tool $cflags "$source" -o "/tmp/$$.$source_fn.bin"
$native_tool $cflags $cflags_native "$source" -o "/tmp/$$.$source_fn.bin"
echo "Running native binary..."
out_native=$("/tmp/$$.$source_fn.bin")
if [ "$out_darwin" != "$out_native" ]; then
echo "*** ERROR: Output doesn't match!"
tput setaf 1
tput bold
echo "*** ERROR: Outputs do not match!"
tput sgr0
echo "---"
tput setaf 3
echo "Darwin build:"
tput sgr0
echo "---"
echo "$out_darwin"
echo "---"
tput setaf 3
echo "Native build:"
tput sgr0
echo "---"
echo "$out_native"
exit 1
fi
rm -f "/tmp/$$.$source_fn.bin"
tput setaf 2
echo "Everything OK, outputs match"
tput sgr0
}
if [[ "$0" == *runtest32 ]]; then
@ -69,8 +100,19 @@ if [[ "$0" == *runtest32 ]]; then
DYLD="dyld32"
echo "32-bit mode"
fi
if [[ "$0" == *runtest64 ]]; then
DYLD="dyld64"
echo "64-bit mode"
fi
for test in "$@"; do
runtest "$test"
done
tput bold
tput setaf 2
echo '====='
echo 'ALL GOOD'
echo '====='
tput sgr0

1
tests/runtest64 Symbolic link
View File

@ -0,0 +1 @@
runtest

13
tests/src/ncurses_acs.c Normal file
View File

@ -0,0 +1,13 @@
// CFLAGS: -lncurses
#include <curses.h>
int main()
{
int v;
initscr();
v = acs_map['x'];
endwin();
printf("%d\n", v);
return 0;
}

View File

@ -1,4 +1,3 @@
// CFLAGS: -lobjc
#include <stdio.h>
#import <Foundation/NSObject.h>
@ -11,16 +10,27 @@
@implementation helloclass
- (void)doHello:(int)a :(int)b :(int)c :(int)d :(int)e :(int)f :(int)g
{
puts("Hello world");
printf("Hello world, %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g);
}
@end
void run()
{
helloclass* c = [helloclass alloc];
puts("After alloc");
[c init];
puts("After init");
[c doHello:1:2:3:4:5:6:7];
puts("After hello");
[c release];
puts("After release");
}
int main()
{
helloclass* c = [[helloclass alloc] init];
[c doHellox:1:2:3:4:5:6:7];
[c release];
return 0;
run();
run();
return 0;
}

View File

@ -0,0 +1,23 @@
#include <stdio.h>
#import <Foundation/NSObject.h>
@interface helloclass : NSObject {
}
+ (void)doHello:(int)a :(int)b :(int)c :(int)d :(int)e :(int)f :(int)g;
@end
@implementation helloclass
+ (void)doHello:(int)a :(int)b :(int)c :(int)d :(int)e :(int)f :(int)g
{
printf("Hello world, %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g);
}
@end
int main()
{
[helloclass doHello:1:2:3:4:5:6:7];
return 0;
}

34
tests/src/objc_ivars.m Normal file
View File

@ -0,0 +1,34 @@
#include <stdio.h>
#import <Foundation/NSObject.h>
@interface helloclass : NSObject {
// We actually need to have two variables here, or the instance sizes won't match
// It seems that the instance size is not rounded up to 4/8 for types generated at runtime
@public int variable, var2;
}
- (void)doHello;
@end
@implementation helloclass
// @synthesize variable;
- (void)doHello
{
printf("Hello world, %d\n", variable);
}
@end
int main()
{
printf("instance size: %d\n", (int) class_getInstanceSize([helloclass class]));
helloclass* c = [helloclass new];
puts("after alloc");
c->variable = 3;
puts("after ivarSet");
[c doHello];
puts("after doHello");
[c release];
return 0;
}

32
tests/src/objc_stret.m Normal file
View File

@ -0,0 +1,32 @@
#include <stdio.h>
#import <Foundation/NSObject.h>
struct st
{
float f1;
int buf[50];
};
@interface helloclass : NSObject {
}
+ (struct st)mult:(float)a :(float)b;
@end
@implementation helloclass
+ (struct st)mult:(float)a :(float)b
{
struct st s;
s.f1 = a+b;
return s;
}
@end
int main()
{
struct st v = [helloclass mult:1.0f:2.0f];
printf("%f\n", v.f1);
return 0;
}