Merge branch 'master' into ci

main
Niels Grewe 10 years ago
commit 28a218451d

@ -1,4 +1,4 @@
GNUstep Objective-C Runtime 1.8 GNUstep Objective-C Runtime 1.x
=============================== ===============================
This the ninth official release of the GNUstep Objective-C runtime (a.k.a. This the ninth official release of the GNUstep Objective-C runtime (a.k.a.
@ -6,19 +6,23 @@ libobjc2). This runtime was designed to support the features of modern
dialects of Objective-C for use with GNUstep and other Objective-C programs. dialects of Objective-C for use with GNUstep and other Objective-C programs.
Highlights of this release include: Highlights of this release include:
- Added API for tracing, allowing interposition on all message sends matching a - Improved the dispatch table representation to improve performance and cache
given selector. usage on the fast path.
- Numerous bug fixes and stability improvements. - The implementation of `imp_implementationWithBlock`, the function that allows
blocks to be used as methods, no longer requires physical pages to be mapped
both writeable and executable.
You may obtain the code for this release from git and use the 1.8 branch: - Numerous improvements to the interaction between runtime functions and ARC.
You may obtain the code for this release from git and use the 1.x branch:
https://github.com/gnustep/libobjc2.git https://github.com/gnustep/libobjc2.git
Alternatively, a tarball is available from: Alternatively, a tarball is available from:
https://github.com/gnustep/libobjc2/archive/v1.8.zip https://github.com/gnustep/libobjc2/archive/v1.x.zip
https://github.com/gnustep/libobjc2/archive/v1.8.tar.gz https://github.com/gnustep/libobjc2/archive/v1.x.tar.gz
The runtime library is responsible for implementing the core features of the The runtime library is responsible for implementing the core features of the
object model, as well as exposing introspection features to the user. The object model, as well as exposing introspection features to the user. The

@ -126,7 +126,7 @@ else ()
add_definitions(-DNO_LEGACY) add_definitions(-DNO_LEGACY)
endif () endif ()
find_package(LLVM QUIET) #find_package(LLVM QUIET)
set(DEFAULT_ENABLE_LLVM ${LLVM_FOUND}) set(DEFAULT_ENABLE_LLVM ${LLVM_FOUND})
if (DEFAULT_ENABLE_LLVM) if (DEFAULT_ENABLE_LLVM)
exec_program(llvm-config exec_program(llvm-config

@ -1,107 +0,0 @@
# Check to see if GNUstep-config is available.
ifeq ($(GNUSTEP_MAKEFILES),)
GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null)
endif
ifeq ($(GNUSTEP_MAKEFILES),)
#
# Start of section for building without GNUstep
#
$(warning GNUstep not found -\
building for standalone installation.)
include Makefile
#
# End of section for building without GNUstep-make
#
else
#
# Start of GNUstep specific section.
#
$(warning GNUstep found -\
building for install in the GNUstep filesystem.)
PACKAGE_NAME = gnustep-objc2
SVN_MODULE_NAME = libobjc2
SVN_BASE_URL = svn+ssh://svn.gna.org/svn/gnustep/libs
SVN_TAG_NAME=objc2
GNUSTEP_INSTALLATION_DOMAIN ?= LOCAL
ifeq ($(messages),yes)
SILENT ?=
endif
INSTALL := dummy_install
include Makefile
include $(GNUSTEP_MAKEFILES)/names.make
# Hack to support -03 for Clang and get the __sync_* GCC builtins work
# -O3 requires -march=i586 on Linux x86-32, otherwise Clang compiles
# programs that segfaults if -fobjc-nonfragile-abi is used.
ifeq ($(findstring linux, $(GNUSTEP_TARGET_OS)), linux)
ifeq ($(GNUSTEP_TARGET_CPU), ix86)
CFLAGS += -march=i586
endif
endif
# Hack to get mingw to provide declaration for strdup (since it is non-standard)
ifeq ($(GNUSTEP_TARGET_OS), mingw32)
${LIBOBJC}_CPPFLAGS += -U__STRICT_ANSI__
endif
# Shouldn't be needed starting with OpenBSD 5.2
ifeq ($(findstring openbsd, `$CC -dumpmachine`), openbsd)
LDFLAGS += -pthread
else
LDFLAGS += -lpthread
endif
LIB_DIR := $(shell gnustep-config --variable=GNUSTEP_$(GNUSTEP_INSTALLATION_DOMAIN)_LIBRARIES 2>/dev/null)
ifeq ($(LIB_DIR),)
LIB_DIR := $(shell gmake -s -f "$(GNUSTEP_MAKEFILES)/empty.make" print-gnustep-install-libraries GNUSTEP_INSTALLATION_DOMAIN=$(GNUSTEP_INSTALLATION_DOMAIN) quiet=yes 2>/dev/null)
endif
ifeq ($(LIB_DIR),)
$(error Unable to use gnustep-config to get install directory - is gnustep-config in your PATH?)
endif
HEADER_DIR := $(shell gnustep-config --variable=GNUSTEP_$(GNUSTEP_INSTALLATION_DOMAIN)_HEADERS 2>/dev/null)
ifeq ($(HEADER_DIR),)
HEADER_DIR := $(shell gmake -s -f "$(GNUSTEP_MAKEFILES)/empty.make" print-gnustep-install-headers GNUSTEP_INSTALLATION_DOMAIN=$(GNUSTEP_INSTALLATION_DOMAIN) quiet=yes 2>/dev/null)
endif
ifeq ($(HEADER_DIR),)
$(error Unable to use gnustep-config to get install directory - is gnustep-config in your PATH?)
endif
install: all
$(SILENT)echo Installing libraries...
$(SILENT)install -d $(LIB_DIR)
$(SILENT)install -m 444 $(STRIP) $(LIBOBJC).so.$(VERSION) $(LIB_DIR)
$(SILENT)install -m 444 $(STRIP) $(LIBOBJCXX).so.$(VERSION) $(LIB_DIR)
$(SILENT)install -m 444 $(STRIP) $(LIBOBJC).a $(LIB_DIR)
$(SILENT)echo Creating symbolic links...
$(SILENT)ln -sf $(LIBOBJC).so.$(VERSION) $(LIB_DIR)/$(LIBOBJC).so
$(SILENT)ln -sf $(LIBOBJC).so.$(VERSION) $(LIB_DIR)/$(LIBOBJC).so.$(MAJOR_VERSION)
$(SILENT)ln -sf $(LIBOBJC).so.$(VERSION) $(LIB_DIR)/$(LIBOBJC).so.$(MAJOR_VERSION).$(MINOR_VERSION)
$(SILENT)ln -sf $(LIBOBJCXX).so.$(VERSION) $(LIB_DIR)/$(LIBOBJCXX).so
$(SILENT)ln -sf $(LIBOBJCXX).so.$(VERSION) $(LIB_DIR)/$(LIBOBJCXX).so.$(MAJOR_VERSION)
$(SILENT)ln -sf $(LIBOBJCXX).so.$(VERSION) $(LIB_DIR)/$(LIBOBJCXX).so.$(MAJOR_VERSION).$(MINOR_VERSION)
$(SILENT)echo Installing headers...
$(SILENT)install -d $(HEADER_DIR)/objc
$(SILENT)install -m 444 objc/*.h $(HEADER_DIR)/objc
$(SILENT)install -m 444 objc/*.h $(HEADER_DIR)/objc
$(SILENT)echo "To use the newly installed objc2 library with GNUstep,"
$(SILENT)echo "please change to your gnustep-make directory and"
$(SILENT)echo "type 'configure; make install' to tell gnustep-make"
$(SILENT)echo "to use the new library (you will then need to configure"
$(SILENT)echo "and build gnustep-base to use the new library too)."
distclean: clean
#
# End of GNUstep-make specific section.
#
endif

@ -1,153 +0,0 @@
.POSIX:
.SUFFIXES: .cc .c .m .o .S
MAJOR_VERSION = 4
MINOR_VERSION = 6
SUBMINOR_VERSION = 0
VERSION ?= $(MAJOR_VERSION).$(MINOR_VERSION).$(SUBMINOR_VERSION)
LIBOBJCLIBNAME=objc
LIBOBJC=libobjc
LIBOBJCXX=libobjcxx
INSTALL ?= install
SILENT ?= @
CFLAGS += -std=gnu99 -fPIC -fexceptions
#CFLAGS += -Wno-deprecated-objc-isa-usage
CXXFLAGS += -fPIC -fexceptions
CPPFLAGS += -DTYPE_DEPENDENT_DISPATCH -DGNUSTEP
CPPFLAGS += -D__OBJC_RUNTIME_INTERNAL__=1 -D_XOPEN_SOURCE=500 -D__BSD_VISIBLE=1 -D_BSD_SOURCE=1
ASMFLAGS += `if $(CC) -v 2>&1| grep -q 'clang' ; then echo -no-integrated-as ; fi`
THE_LD=`if [ "$(LD)" = "" ]; then echo "ld"; else echo "$(LD)"; fi`
STRIP=`if [ "$(strip)" = "yes" ] ; then echo -s ; fi`
# Suppress warnings about incorrect selectors
CPPFLAGS += -DNO_SELECTOR_MISMATCH_WARNINGS
# Some helpful flags for debugging.
#CPPFLAGS += -g -O0 -fno-inline
CPPFLAGS += -O3
PREFIX?= /usr/local
LIB_DIR= ${PREFIX}/lib
HEADER_DIR= ${PREFIX}/include
OBJCXX_OBJECTS = \
objcxx_eh.o
OBJECTS = \
NSBlocks.o\
Protocol2.o\
abi_version.o\
alias_table.o\
arc.o\
associate.o\
blocks_runtime.o\
block_to_imp.o\
block_trampolines.o\
objc_msgSend.o\
caps.o\
category_loader.o\
class_table.o\
dtable.o\
eh_personality.o\
encoding2.o\
gc_none.o\
hash_table.o\
hooks.o\
ivar.o\
legacy_malloc.o\
loader.o\
mutation.o\
properties.o\
protocol.o\
runtime.o\
sarray2.o\
selector_table.o\
sendmsg2.o\
statics_loader.o\
toydispatch.o
all: warning $(LIBOBJC).a $(LIBOBJCXX).so.$(VERSION)
@echo '********************************************************************************'
@echo '*********************************** WARNING ************************************'
@echo '********************************************************************************'
@echo The Makfile build is deprecated, and should not be used.
@echo Please use cmake. The recommended procedure is:
@echo $ mkdir Build
@echo $ cd Build
@echo cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
@echo "make && sudo -E make install"
warning:
@echo '********************************************************************************'
@echo '*********************************** WARNING ************************************'
@echo '********************************************************************************'
@echo The Makfile build is deprecated, and should not be used.
@echo Please use cmake. The recommended procedure is:
@echo $ mkdir Build
@echo $ cd Build
@echo cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
@echo "make && sudo -E make install"
$(LIBOBJCXX).so.$(VERSION): $(LIBOBJC).so.$(VERSION) $(OBJCXX_OBJECTS)
$(SILENT)echo Linking shared Objective-C++ runtime library...
$(SILENT)$(CXX) -shared \
-Wl,-soname=$(LIBOBJCXX).so.$(MAJOR_VERSION) $(LDFLAGS) \
-o $@ $(OBJCXX_OBJECTS)
$(LIBOBJC).so.$(VERSION): $(OBJECTS)
$(SILENT)echo Linking shared Objective-C runtime library...
$(SILENT)$(CC) -shared -rdynamic \
-Wl,-soname=$(LIBOBJC).so.$(MAJOR_VERSION) $(LDFLAGS) \
-o $@ $(OBJECTS)
$(LIBOBJC).a: $(OBJECTS)
$(SILENT)echo Linking static Objective-C runtime library...
$(SILENT)$(THE_LD) -r -s -o $@ $(OBJECTS)
.cc.o: Makefile
$(SILENT)echo Compiling `basename $<`...
$(SILENT)$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
.c.o: Makefile
$(SILENT)echo Compiling `basename $<`...
$(SILENT)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
.m.o: Makefile
$(SILENT)echo Compiling `basename $<`...
$(SILENT)$(CC) $(CPPFLAGS) $(CFLAGS) -fobjc-exceptions -c $< -o $@
.S.o: Makefile
$(SILENT)echo Assembling `basename $<`...
$(SILENT)$(CC) $(CPPFLAGS) $(ASMFLAGS) -c $< -o $@
$(INSTALL): all
$(SILENT)echo Installing libraries...
$(SILENT)install -d $(LIB_DIR)
$(SILENT)install -m 444 $(STRIP) $(LIBOBJC).so.$(VERSION) $(LIB_DIR)
$(SILENT)install -m 444 $(STRIP) $(LIBOBJCXX).so.$(VERSION) $(LIB_DIR)
$(SILENT)install -m 444 $(STRIP) $(LIBOBJC).a $(LIB_DIR)
$(SILENT)echo Creating symbolic links...
$(SILENT)ln -sf $(LIBOBJC).so.$(VERSION) $(LIB_DIR)/$(LIBOBJC).so
$(SILENT)ln -sf $(LIBOBJC).so.$(VERSION) $(LIB_DIR)/$(LIBOBJC).so.$(MAJOR_VERSION)
$(SILENT)ln -sf $(LIBOBJC).so.$(VERSION) $(LIB_DIR)/$(LIBOBJC).so.$(MAJOR_VERSION).$(MINOR_VERSION)
$(SILENT)ln -sf $(LIBOBJCXX).so.$(VERSION) $(LIB_DIR)/$(LIBOBJCXX).so
$(SILENT)ln -sf $(LIBOBJCXX).so.$(VERSION) $(LIB_DIR)/$(LIBOBJCXX).so.$(MAJOR_VERSION)
$(SILENT)ln -sf $(LIBOBJCXX).so.$(VERSION) $(LIB_DIR)/$(LIBOBJCXX).so.$(MAJOR_VERSION).$(MINOR_VERSION)
$(SILENT)echo Installing headers...
$(SILENT)install -d $(HEADER_DIR)/objc
$(SILENT)install -m 444 objc/*.h $(HEADER_DIR)/objc
clean:
$(SILENT)echo Cleaning...
$(SILENT)rm -f $(OBJECTS)
$(SILENT)rm -f $(OBJCXX_OBJECTS)
$(SILENT)rm -f $(LIBOBJC).so.$(VERSION)
$(SILENT)rm -f $(LIBOBJCXX).so.$(VERSION)
$(SILENT)rm -f $(LIBOBJC).a

@ -20,6 +20,8 @@
- (id)self { return self; } - (id)self { return self; }
@end @end
@implementation Protocol2 @end @implementation Protocol2 @end
@interface __IncompleteProtocol : Protocol2 @end
@implementation __IncompleteProtocol @end
/** /**
* This class exists for the sole reason that the legacy GNU ABI did not * This class exists for the sole reason that the legacy GNU ABI did not

@ -21,6 +21,7 @@ __attribute__((objc_root_class))
+(struct big)sret; +(struct big)sret;
@end @end
int main(void) int main(void)
{ {
__block int b = 0; __block int b = 0;
@ -32,12 +33,16 @@ int main(void)
char *type = block_copyIMPTypeEncoding_np(blk); char *type = block_copyIMPTypeEncoding_np(blk);
assert(NULL != type); assert(NULL != type);
class_addMethod((objc_getMetaClass("Foo")), @selector(count:), imp, type); class_addMethod((objc_getMetaClass("Foo")), @selector(count:), imp, type);
Class cls = objc_getClass("Foo");
assert(2 == ((int(*)(id,SEL,int))imp)(cls, @selector(count:), 2));
free(type); free(type);
assert(2 == [Foo count: 2]);
assert(4 == [Foo count: 2]); assert(4 == [Foo count: 2]);
assert(6 == [Foo count: 2]); assert(6 == [Foo count: 2]);
assert(imp_getBlock(imp) == (blk)); assert(imp_getBlock(imp) == (blk));
imp_removeBlock(blk); IMP imp2 = imp_implementationWithBlock(blk);
assert(imp != imp2);
imp_removeBlock(imp);
assert(imp_getBlock(imp) != (blk));
blk = ^(id self) { blk = ^(id self) {
struct big b = {1, 2, 3, 4, 5}; struct big b = {1, 2, 3, 4, 5};

@ -19,11 +19,13 @@ set(TESTS
NestedExceptions.m NestedExceptions.m
PropertyAttributeTest.m PropertyAttributeTest.m
PropertyIntrospectionTest.m PropertyIntrospectionTest.m
PropertyIntrospectionTest2.m PropertyIntrospectionTest2_arc.m
ProtocolCreation.m ProtocolCreation.m
ResurrectInDealloc_arc.m ResurrectInDealloc_arc.m
RuntimeTest.m RuntimeTest.m
WeakBlock_arc.m
WeakReferences_arc.m WeakReferences_arc.m
ivar_arc.m
objc_msgSend.m objc_msgSend.m
msgInterpose.m msgInterpose.m
NilException.m NilException.m
@ -32,26 +34,26 @@ set(TESTS
# Function for adding a test. This takes the name of the test and the list of # Function for adding a test. This takes the name of the test and the list of
# source files as arguments. # source files as arguments.
function(addtest_flags TEST FLAGS TEST_SOURCE) function(addtest_flags TEST_NAME FLAGS TEST_SOURCE)
if (TEST MATCHES ".*_arc") if (${TEST_NAME} MATCHES ".*_arc")
# Only compile the main file with ARC # Only compile the main file with ARC
set_source_files_properties(${TEST_SOURCE} set_source_files_properties(${TEST_SOURCE}
COMPILE_FLAGS "-fobjc-arc") COMPILE_FLAGS "-fobjc-arc")
# Add the ARC-incompatible definitions of the test class. # Add the ARC-incompatible definitions of the test class.
list(APPEND TEST_SOURCE "Test.m") list(APPEND TEST_SOURCE "Test.m")
endif() endif()
add_executable(${TEST} ${TEST_SOURCE}) add_executable(${TEST_NAME} ${TEST_SOURCE})
add_test(${TEST} ${TEST}) add_test(${TEST_NAME} ${TEST_NAME})
set(ARC "") set(ARC "")
set_target_properties(${TEST} PROPERTIES set_target_properties(${TEST_NAME} PROPERTIES
INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}" INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}"
COMPILE_FLAGS "-fobjc-runtime=gnustep-1.7 -fblocks ${FLAGS}" COMPILE_FLAGS "-fobjc-runtime=gnustep-1.7 -fblocks ${FLAGS}"
LINKER_LANGUAGE C LINKER_LANGUAGE C
) )
set_property(TEST ${TEST} PROPERTY set_property(TEST ${TEST_NAME} PROPERTY
ENVIRONMENT "LD_LIBRARY_PATH=" ENVIRONMENT "LD_LIBRARY_PATH="
) )
target_link_libraries(${TEST} objc) target_link_libraries(${TEST_NAME} objc)
endfunction(addtest_flags) endfunction(addtest_flags)
foreach(TEST_SOURCE ${TESTS}) foreach(TEST_SOURCE ${TESTS})

@ -7,7 +7,7 @@
#pragma GCC diagnostic ignored "-Wobjc-property-no-attribute" #pragma GCC diagnostic ignored "-Wobjc-property-no-attribute"
enum FooManChu { FOO, MAN, CHU }; enum FooManChu { FOO, MAN, CHU };
struct YorkshireTeaStruct { int pot; char lady; }; struct YorkshireTeaStruct { int pot; signed char lady; };
typedef struct YorkshireTeaStruct YorkshireTeaStructType; typedef struct YorkshireTeaStruct YorkshireTeaStructType;
union MoneyUnion { float alone; double down; }; union MoneyUnion { float alone; double down; };
@ -22,7 +22,7 @@ __attribute__((objc_root_class))
{ {
@public @public
Class isa; Class isa;
char charDefault; signed char charDefault;
double doubleDefault; double doubleDefault;
enum FooManChu enumDefault; enum FooManChu enumDefault;
float floatDefault; float floatDefault;
@ -43,18 +43,18 @@ __attribute__((objc_root_class))
int intReadonlyGetter; int intReadonlyGetter;
int intReadwrite; int intReadwrite;
int intAssign; int intAssign;
id idDefault; __unsafe_unretained id idDefault;
id idRetain; id idRetain;
id idCopy; id idCopy;
id idWeak; __weak id idWeak;
id idStrong; id idStrong;
int intNonatomic; int intNonatomic;
id idReadonlyCopyNonatomic; id idReadonlyCopyNonatomic;
id idReadonlyRetainNonatomic; id idReadonlyRetainNonatomic;
id idReadonlyWeakNonatomic; __weak id idReadonlyWeakNonatomic;
id _idOther; id _idOther;
} }
@property char charDefault; @property signed char charDefault;
@property double doubleDefault; @property double doubleDefault;
@property enum FooManChu enumDefault; @property enum FooManChu enumDefault;
@property float floatDefault; @property float floatDefault;
@ -74,7 +74,7 @@ __attribute__((objc_root_class))
@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; @property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter;
@property(readwrite) int intReadwrite; @property(readwrite) int intReadwrite;
@property(assign) int intAssign; @property(assign) int intAssign;
@property id idDefault; @property(unsafe_unretained) id idDefault;
@property(retain) id idRetain; @property(retain) id idRetain;
@property(copy) id idCopy; @property(copy) id idCopy;
@property(weak) id idWeak; @property(weak) id idWeak;
@ -129,10 +129,11 @@ __attribute__((objc_root_class))
@synthesize idOther = _idOther; @synthesize idOther = _idOther;
@dynamic idDynamic; @dynamic idDynamic;
@dynamic idDynamicGetterSetter; @dynamic idDynamicGetterSetter;
- (void)_ARCCompliantRetainRelease {}
@end @end
@protocol ProtocolTest @protocol ProtocolTest
@property char charDefault; @property signed char charDefault;
@property double doubleDefault; @property double doubleDefault;
@property enum FooManChu enumDefault; @property enum FooManChu enumDefault;
@property float floatDefault; @property float floatDefault;
@ -152,7 +153,7 @@ __attribute__((objc_root_class))
@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; @property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter;
@property(readwrite) int intReadwrite; @property(readwrite) int intReadwrite;
@property(assign) int intAssign; @property(assign) int intAssign;
@property id idDefault; @property(unsafe_unretained) id idDefault;
@property(retain) id idRetain; @property(retain) id idRetain;
@property(copy) id idCopy; @property(copy) id idCopy;
@property(weak) id idWeak; @property(weak) id idWeak;
@ -172,7 +173,7 @@ __attribute__((objc_root_class))
@interface PropertyProtocolTest <ProtocolTest> @interface PropertyProtocolTest <ProtocolTest>
{ {
Class isa; Class isa;
char charDefault; signed char charDefault;
double doubleDefault; double doubleDefault;
enum FooManChu enumDefault; enum FooManChu enumDefault;
float floatDefault; float floatDefault;
@ -193,15 +194,15 @@ __attribute__((objc_root_class))
int intReadonlyGetter; int intReadonlyGetter;
int intReadwrite; int intReadwrite;
int intAssign; int intAssign;
id idDefault; __unsafe_unretained id idDefault;
id idRetain; id idRetain;
id idCopy; id idCopy;
id idWeak; __weak id idWeak;
id idStrong; id idStrong;
int intNonatomic; int intNonatomic;
id idReadonlyCopyNonatomic; id idReadonlyCopyNonatomic;
id idReadonlyRetainNonatomic; id idReadonlyRetainNonatomic;
id idReadonlyWeakNonatomic; __weak id idReadonlyWeakNonatomic;
id _idOther; id _idOther;
} }
@end @end
@ -239,6 +240,7 @@ __attribute__((objc_root_class))
@synthesize idOther = _idOther; @synthesize idOther = _idOther;
@dynamic idDynamic; @dynamic idDynamic;
@dynamic idDynamicGetterSetter; @dynamic idDynamicGetterSetter;
- (void)_ARCCompliantRetainRelease {}
@end @end
#define ATTR(n, v) (objc_property_attribute_t){(n), (v)} #define ATTR(n, v) (objc_property_attribute_t){(n), (v)}
@ -473,17 +475,17 @@ static int intDefault2Getter(id self, SEL _cmd) {
static void intDefault2Setter(id self, SEL _cmd, int value) { static void intDefault2Setter(id self, SEL _cmd, int value) {
Ivar ivar = class_getInstanceVariable(objc_getClass("PropertyTest"), "intDefault"); Ivar ivar = class_getInstanceVariable(objc_getClass("PropertyTest"), "intDefault");
object_setIvar(self, ivar, (id)(intptr_t)value); object_setIvar(self, ivar, (__bridge id)(void*)(intptr_t)value);
} }
static struct YorkshireTeaStruct* structDefault2Getter(id self, SEL _cmd) { static struct YorkshireTeaStruct structDefault2Getter(id self, SEL _cmd) {
Ivar ivar = class_getInstanceVariable(objc_getClass("PropertyTest"), "structDefault"); struct YorkshireTeaStruct *s;
return (struct YorkshireTeaStruct*)object_getIvar(self, ivar); object_getInstanceVariable(self, "structDefault", (void**)&s);
return *s;
} }
void structDefault2Setter(id self, SEL _cmd, struct YorkshireTeaStruct* value) { void structDefault2Setter(id self, SEL _cmd, struct YorkshireTeaStruct value) {
Ivar ivar = class_getInstanceVariable(objc_getClass("PropertyTest"), "structDefault"); object_setInstanceVariable(self, "structDefault", &value);
object_setIvar(self, ivar, (id)value);
} }
int main(void) int main(void)
@ -666,6 +668,7 @@ int main(void)
ATTR("V", "structDefault"))); ATTR("V", "structDefault")));
PropertyTest* t = class_createInstance(testClass, 0); PropertyTest* t = class_createInstance(testClass, 0);
assert(t != nil);
object_setClass(t, testClass); object_setClass(t, testClass);
t.intDefault = 2; t.intDefault = 2;
assert(t.intDefault == 2); assert(t.intDefault == 2);

@ -0,0 +1,15 @@
#include "Test.h"
int main(int argc, const char * argv[])
{
id __weak ref;
@autoreleasepool {
__block int val;
id block = ^() { return val++; };
assert(block != nil);
ref = block;
assert(ref != nil);
}
assert(ref == nil);
return 0;
}

@ -0,0 +1,52 @@
#include "Test.h"
#include "../objc/runtime.h"
@interface Foo : Test
{
@public
__weak id w;
__unsafe_unretained id u;
__strong id s;
}
@end
@implementation Foo @end
@interface Dealloc : Test
@end
int dealloc = 0;
@implementation Dealloc
- (void)dealloc
{
dealloc++;
}
@end
void setIvar(id obj, const char * name, id val)
{
object_setIvar(obj, class_getInstanceVariable(object_getClass(obj), name), val);
}
int main(void)
{
Foo *f = [Foo new];
Dealloc *d = [Dealloc new];
__unsafe_unretained Dealloc *dead;
setIvar(f, "w", d);
assert(f->w == d);
assert(dealloc == 0);
d = 0;
assert(dealloc == 1);
assert(f->w == nil);
dealloc = 0;
d = [Dealloc new];
dead = d;
setIvar(f, "s", d);
assert(dealloc == 0);
assert(f->s == d);
d = nil;
assert(dealloc == 0);
assert(f->s == dead);
setIvar(f, "s", nil);
assert(dealloc == 1);
assert(f->s == nil);
return 0;
}

@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include "../objc/runtime.h" #include "../objc/runtime.h"
#include "../objc/hooks.h"
//#define assert(x) if (!(x)) { printf("Failed %d\n", __LINE__); } //#define assert(x) if (!(x)) { printf("Failed %d\n", __LINE__); }
@ -23,7 +24,31 @@ Class TestCls;
__attribute__((objc_root_class)) __attribute__((objc_root_class))
#endif #endif
#endif #endif
@interface Test { id isa; }@end @interface Test { id isa; } @end
@interface Test (Dynamic)
+ (void)manyArgs: (int)a0
: (int) a1
: (int) a2
: (int) a3
: (int) a4
: (int) a5
: (int) a6
: (int) a7
: (int) a8
: (int) a9
: (int) a10
: (float) f0
: (float) f1
: (float) f2
: (float) f3
: (float) f4
: (float) f5
: (float) f6
: (float) f7
: (float) f8
: (float) f9
: (float) f10;
@end
@implementation Test @implementation Test
- foo - foo
{ {
@ -70,8 +95,87 @@ __attribute__((objc_root_class))
} }
+ nothing { return 0; } + nothing { return 0; }
@end @end
int forwardcalls;
void fwdMany(id self,
SEL _cmd,
int a0,
int a1,
int a2,
int a3,
int a4,
int a5,
int a6,
int a7,
int a8,
int a9,
int a10,
float f0,
float f1,
float f2,
float f3,
float f4,
float f5,
float f6,
float f7,
float f8,
float f9,
float f10)
{
forwardcalls++;
assert(self == objc_getClass("Test"));
if (sel_isEqual(_cmd, sel_registerName("manyArgs:::::::::::::::::::::")))
assert(a0 == 0);
assert(a1 == 1);
assert(a2 == 2);
assert(a3 == 3);
assert(a4 == 4);
assert(a5 == 5);
assert(a6 == 6);
assert(a7 == 7);
assert(a8 == 8);
assert(a9 == 9);
assert(a10 == 10);
assert(f0 == 0);
assert(f1 == 1);
assert(f2 == 2);
assert(f3 == 3);
assert(f4 == 4);
assert(f5 == 5);
assert(f6 == 6);
assert(f7 == 7);
assert(f8 == 8);
assert(f9 == 9);
assert(f10 == 10);
}
void fwd(void)
{
forwardcalls++;
}
IMP forward(id o, SEL s)
{
assert(o == objc_getClass("Test"));
if (sel_isEqual(s, sel_registerName("missing")))
{
return (IMP)fwd;
}
return (IMP)fwdMany;
}
static struct objc_slot slot;
struct objc_slot *forwardslot(id o, SEL s)
{
slot.method = (IMP)fwd;
return &slot;
}
int main(void) int main(void)
{ {
__objc_msg_forward2 = forward;
__objc_msg_forward3 = forward_slot;
TestCls = objc_getClass("Test"); TestCls = objc_getClass("Test");
int exceptionThrown = 0; int exceptionThrown = 0;
@try { @try {
@ -85,6 +189,7 @@ int main(void)
assert((id)0x42 == objc_msgSend(TestCls, @selector(foo))); assert((id)0x42 == objc_msgSend(TestCls, @selector(foo)));
objc_msgSend(TestCls, @selector(nothing)); objc_msgSend(TestCls, @selector(nothing));
objc_msgSend(TestCls, @selector(missing)); objc_msgSend(TestCls, @selector(missing));
assert(forwardcalls == 1);
assert(0 == objc_msgSend(0, @selector(nothing))); assert(0 == objc_msgSend(0, @selector(nothing)));
id a = objc_msgSend(objc_getClass("Test"), @selector(foo)); id a = objc_msgSend(objc_getClass("Test"), @selector(foo));
assert((id)0x42 == a); assert((id)0x42 == a);
@ -114,33 +219,38 @@ int main(void)
assert(0 == [f dzero]); assert(0 == [f dzero]);
assert(0 == [f ldzero]); assert(0 == [f ldzero]);
assert(0 == [f fzero]); assert(0 == [f fzero]);
[TestCls manyArgs: 0 : 1 : 2 : 3: 4: 5: 6: 7: 8: 9: 10 : 0 : 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10];
assert(forwardcalls == 2);
#ifdef BENCHMARK #ifdef BENCHMARK
const int iterations = 1000000000;
double times[3];
clock_t c1, c2; clock_t c1, c2;
c1 = clock(); c1 = clock();
for (int i=0 ; i<100000000 ; i++) for (int i=0 ; i<iterations ; i++)
{ {
[TestCls nothing]; [TestCls nothing];
} }
c2 = clock(); c2 = clock();
printf("Traditional message send took %f seconds. \n", times[0] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC); fprintf(stderr, "Traditional message send took %f seconds. \n", times[0]);
c1 = clock(); c1 = clock();
for (int i=0 ; i<100000000 ; i++) for (int i=0 ; i<iterations ; i++)
{ {
objc_msgSend(TestCls, @selector(nothing)); objc_msgSend(TestCls, @selector(nothing));
} }
c2 = clock(); c2 = clock();
printf("objc_msgSend() message send took %f seconds. \n", times[1] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC); fprintf(stderr, "objc_msgSend() message send took %f seconds. \n", times[1]);
IMP nothing = objc_msg_lookup(TestCls, @selector(nothing)); IMP nothing = objc_msg_lookup(TestCls, @selector(nothing));
c1 = clock(); c1 = clock();
for (int i=0 ; i<100000000 ; i++) for (int i=0 ; i<iterations ; i++)
{ {
nothing(TestCls, @selector(nothing)); nothing(TestCls, @selector(nothing));
} }
c2 = clock(); c2 = clock();
printf("Direct IMP call took %f seconds. \n", times[2] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC); fprintf(stderr, "Direct IMP call took %f seconds. \n", times[2]);
printf("%f\t%f\t%f\n", times[0], times[1], times[2]);
#endif #endif
return 0; return 0;
} }

35
arc.m

@ -525,10 +525,27 @@ void* block_load_weak(void *block);
id objc_storeWeak(id *addr, id obj) id objc_storeWeak(id *addr, id obj)
{ {
id old = *addr; id old = *addr;
intptr_t *refCount = ((intptr_t*)obj) - 1;
if (obj && *refCount < 0) BOOL isGlobalObject = (obj == nil) || isSmallObject(obj);
Class cls = Nil;
if (!isGlobalObject)
{
cls = classForObject(obj);
// TODO: We probably also want to do the same for constant strings and
// classes.
if (cls == &_NSConcreteGlobalBlock)
{
isGlobalObject = YES;
}
}
if (cls && objc_test_class_flag(cls, objc_class_flag_fast_arc))
{ {
obj = nil; intptr_t *refCount = ((intptr_t*)obj) - 1;
if (obj && *refCount < 0)
{
obj = nil;
cls = Nil;
}
} }
LOCK_FOR_SCOPE(&weakRefLock); LOCK_FOR_SCOPE(&weakRefLock);
if (nil != old) if (nil != old)
@ -553,13 +570,10 @@ id objc_storeWeak(id *addr, id obj)
*addr = obj; *addr = obj;
return nil; return nil;
} }
Class cls = classForObject(obj); if (isGlobalObject)
if (&_NSConcreteGlobalBlock == cls)
{ {
// If this is a global block, it's never deallocated, so secretly make // If this is a global object, it's never deallocated, so secretly make
// this a strong reference // this a strong reference
// TODO: We probably also want to do the same for constant strings and
// classes.
*addr = obj; *addr = obj;
return obj; return obj;
} }
@ -650,6 +664,11 @@ id objc_loadWeakRetained(id* addr)
LOCK_FOR_SCOPE(&weakRefLock); LOCK_FOR_SCOPE(&weakRefLock);
id obj = *addr; id obj = *addr;
if (nil == obj) { return nil; } if (nil == obj) { return nil; }
// Small objects don't need reference count modification
if (isSmallObject(obj))
{
return obj;
}
Class cls = classForObject(obj); Class cls = classForObject(obj);
if (&_NSConcreteMallocBlock == cls) if (&_NSConcreteMallocBlock == cls)
{ {

@ -0,0 +1,13 @@
#ifdef __LP64__
#define DTABLE_OFFSET 64
#define SMALLOBJ_MASK 7
#define SHIFT_OFFSET 0
#define DATA_OFFSET 8
#define SLOT_OFFSET 32
#else
#define DTABLE_OFFSET 32
#define SMALLOBJ_MASK 1
#define SHIFT_OFFSET 0
#define DATA_OFFSET 8
#define SLOT_OFFSET 16
#endif

@ -35,136 +35,195 @@ void __clear_cache(void* start, void* end);
#define PAGE_SIZE 4096 #define PAGE_SIZE 4096
static void *executeBuffer; struct block_header
static void *writeBuffer;
static ptrdiff_t offset;
static mutex_t trampoline_lock;
#ifndef SHM_ANON
static char *tmpPattern;
static void initTmpFile(void)
{ {
char *tmp = getenv("TMPDIR"); void *block;
if (NULL == tmp) void(*fnptr)(void);
{ /**
tmp = "/tmp/"; * On 64-bit platforms, we have 16 bytes for instructions, which ought to
} * be enough without padding. On MIPS, we need
if (0 > asprintf(&tmpPattern, "%s/objc_trampolinesXXXXXXXXXXX", tmp)) * Note: If we add too much padding, then we waste space but have no other
{ * ill effects. If we get this too small, then the assert in
abort(); * `init_trampolines` will fire on library load.
} */
} #if defined(__i386__) || (defined(__mips__) && !defined(__mips_n64))
static int getAnonMemFd(void) uint64_t padding[3];
#elif defined(__mips__)
uint64_t padding[2];
#elif defined(__arm__)
uint64_t padding;
#endif
};
#define HEADERS_PER_PAGE (PAGE_SIZE/sizeof(struct block_header))
/**
* Structure containing a two pages of block trampolines. Each trampoline
* loads its block and target method address from the corresponding
* block_header (one page before the start of the block structure).
*/
struct trampoline_buffers
{ {
const char *pattern = strdup(tmpPattern); struct block_header headers[HEADERS_PER_PAGE];
int fd = mkstemp(pattern); char rx_buffer[PAGE_SIZE];
unlink(pattern); };
free(pattern); _Static_assert(__builtin_offsetof(struct trampoline_buffers, rx_buffer) == PAGE_SIZE,
return fd; "Incorrect offset for read-execute buffer");
} _Static_assert(sizeof(struct trampoline_buffers) == 2*PAGE_SIZE,
#else "Incorrect size for trampoline buffers");
static void initTmpFile(void) {}
static int getAnonMemFd(void) struct trampoline_set
{ {
return shm_open(SHM_ANON, O_CREAT | O_RDWR, 0); struct trampoline_buffers *buffers;
} struct trampoline_set *next;
#endif int first_free;
};
static mutex_t trampoline_lock;
struct wx_buffer struct wx_buffer
{ {
void *w; void *w;
void *x; void *x;
}; };
extern char __objc_block_trampoline;
extern char __objc_block_trampoline_end;
extern char __objc_block_trampoline_sret;
extern char __objc_block_trampoline_end_sret;
PRIVATE void init_trampolines(void) PRIVATE void init_trampolines(void)
{ {
assert(&__objc_block_trampoline_end - &__objc_block_trampoline <= sizeof(struct block_header));
assert(&__objc_block_trampoline_end_sret - &__objc_block_trampoline_sret <= sizeof(struct block_header));
INIT_LOCK(trampoline_lock); INIT_LOCK(trampoline_lock);
initTmpFile();
} }
static struct wx_buffer alloc_buffer(size_t size) static id invalid(id self, SEL _cmd)
{ {
LOCK_FOR_SCOPE(&trampoline_lock); fprintf(stderr, "Invalid block method called for [%s %s]\n",
if ((0 == offset) || (offset + size >= PAGE_SIZE)) class_getName(object_getClass(self)), sel_getName(_cmd));
return nil;
}
static struct trampoline_set *alloc_trampolines(char *start, char *end)
{
struct trampoline_set *metadata = calloc(1, sizeof(struct trampoline_set));
metadata->buffers = valloc(sizeof(struct trampoline_buffers));
for (int i=0 ; i<HEADERS_PER_PAGE ; i++)
{ {
int fd = getAnonMemFd(); metadata->buffers->headers[i].fnptr = (void(*)(void))invalid;
ftruncate(fd, PAGE_SIZE); metadata->buffers->headers[i].block = &metadata->buffers->headers[i+1].block;
void *w = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); char *block = metadata->buffers->rx_buffer + (i * sizeof(struct block_header));
executeBuffer = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0); memcpy(block, start, end-start);
*((void**)w) = writeBuffer;
writeBuffer = w;
offset = sizeof(void*);
} }
struct wx_buffer b = { writeBuffer + offset, executeBuffer + offset }; metadata->buffers->headers[HEADERS_PER_PAGE-1].block = NULL;
offset += size; mprotect(metadata->buffers->rx_buffer, PAGE_SIZE, PROT_READ | PROT_EXEC);
return b; clear_cache(metadata->buffers->rx_buffer, &metadata->buffers->rx_buffer[PAGE_SIZE]);
return metadata;
} }
extern void __objc_block_trampoline; static struct trampoline_set *sret_trampolines;
extern void __objc_block_trampoline_end; static struct trampoline_set *trampolines;
extern void __objc_block_trampoline_sret;
extern void __objc_block_trampoline_end_sret;
IMP imp_implementationWithBlock(void *block) IMP imp_implementationWithBlock(void *block)
{ {
struct Block_layout *b = block; struct Block_layout *b = block;
void *start; void *start;
void *end; void *end;
LOCK_FOR_SCOPE(&trampoline_lock);
struct trampoline_set **setptr;
if ((b->flags & BLOCK_USE_SRET) == BLOCK_USE_SRET) if ((b->flags & BLOCK_USE_SRET) == BLOCK_USE_SRET)
{ {
setptr = &sret_trampolines;
start = &__objc_block_trampoline_sret; start = &__objc_block_trampoline_sret;
end = &__objc_block_trampoline_end_sret; end = &__objc_block_trampoline_end_sret;
} }
else else
{ {
setptr = &trampolines;
start = &__objc_block_trampoline; start = &__objc_block_trampoline;
end = &__objc_block_trampoline_end; end = &__objc_block_trampoline_end;
} }
size_t trampolineSize = end - start; size_t trampolineSize = end - start;
// If we don't have a trampoline intrinsic for this architecture, return a // If we don't have a trampoline intrinsic for this architecture, return a
// null IMP. // null IMP.
if (0 >= trampolineSize) { return 0; } if (0 >= trampolineSize) { return 0; }
block = Block_copy(block);
struct wx_buffer buf = alloc_buffer(trampolineSize + 2*sizeof(void*)); // Allocate some trampolines if this is the first time that we need to do this.
void **out = buf.w; if (*setptr == NULL)
out[0] = (void*)b->invoke; {
out[1] = Block_copy(b); *setptr = alloc_trampolines(start, end);
memcpy(&out[2], start, trampolineSize); }
out = buf.x; for (struct trampoline_set *set=*setptr ; set!=NULL ; set=set->next)
char *newIMP = (char*)&out[2]; {
clear_cache(newIMP, newIMP+trampolineSize); if (set->first_free != -1)
return (IMP)newIMP; {
int i = set->first_free;
struct block_header *h = &set->buffers->headers[i];
struct block_header *next = h->block;
set->first_free = next ? (next - set->buffers->headers) : -1;
assert(set->first_free < HEADERS_PER_PAGE);
assert(set->first_free >= -1);
h->fnptr = (void(*)(void))b->invoke;
h->block = b;
return (IMP)&set->buffers->rx_buffer[i*sizeof(struct block_header)];
}
}
UNREACHABLE("Failed to allocate block");
} }
static void* isBlockIMP(void *anIMP) static int indexForIMP(IMP anIMP, struct trampoline_set **setptr)
{ {
LOCK(&trampoline_lock); for (struct trampoline_set *set=*setptr ; set!=NULL ; set=set->next)
void *e = executeBuffer;
void *w = writeBuffer;
UNLOCK(&trampoline_lock);
while (e)
{ {
if ((anIMP > e) && (anIMP < e + PAGE_SIZE)) if (((char*)anIMP >= set->buffers->rx_buffer) &&
((char*)anIMP < &set->buffers->rx_buffer[PAGE_SIZE]))
{ {
return ((char*)w) + ((char*)anIMP - (char*)e); *setptr = set;
ptrdiff_t offset = (char*)anIMP - set->buffers->rx_buffer;
return offset / sizeof(struct block_header);
} }
e = *(void**)e;
w = *(void**)w;
} }
return 0; return -1;
} }
void *imp_getBlock(IMP anImp) void *imp_getBlock(IMP anImp)
{ {
if (0 == isBlockIMP((void*)anImp)) { return 0; } LOCK_FOR_SCOPE(&trampoline_lock);
return *(((void**)anImp) - 1); struct trampoline_set *set = trampolines;
int idx = indexForIMP(anImp, &set);
if (idx == -1)
{
set = sret_trampolines;
indexForIMP(anImp, &set);
}
if (idx == -1)
{
return NULL;
}
return set->buffers->headers[idx].block;
} }
BOOL imp_removeBlock(IMP anImp) BOOL imp_removeBlock(IMP anImp)
{ {
void *w = isBlockIMP((void*)anImp); LOCK_FOR_SCOPE(&trampoline_lock);
if (0 == w) { return NO; } struct trampoline_set *set = trampolines;
Block_release(((void**)anImp) - 1); int idx = indexForIMP(anImp, &set);
if (idx == -1)
{
set = sret_trampolines;
indexForIMP(anImp, &set);
}
if (idx == -1)
{
return NO;
}
struct block_header *h = &set->buffers->headers[idx];
Block_release(h->block);
h->fnptr = (void(*)(void))invalid;
h->block = set->first_free == -1 ? NULL : &set->buffers->headers[set->first_free];
set->first_free = h - set->buffers->headers;
return YES; return YES;
} }

@ -32,75 +32,86 @@ TYPE_DIRECTIVE(CDECL(__objc_block_trampoline), @function)
.globl CDECL(__objc_block_trampoline_end) .globl CDECL(__objc_block_trampoline_end)
#endif #endif
#if __x86_64 #if __x86_64
#ifdef _WIN64
#define ARG0 %rcx
#define ARG1 %rdx
#define ARG2 %r8
#else
#define ARG0 %rdi
#define ARG1 %rsi
#define ARG2 %rdx
#endif
CDECL(__objc_block_trampoline): CDECL(__objc_block_trampoline):
mov -15(%rip), %rsi # Load the block pointer into the second argument mov -0x1007(%rip), ARG1 # Load the block pointer into the second argument
xchg %rdi, %rsi # Swap the first and second arguments xchg ARG1, ARG0 # Swap the first and second arguments
jmp *-32(%rip) # Call the block function jmp *-0x1008(%rip) # Call the block function
CDECL(__objc_block_trampoline_end): CDECL(__objc_block_trampoline_end):
CDECL(__objc_block_trampoline_sret): CDECL(__objc_block_trampoline_sret):
mov -15(%rip), %rdx # Load the block pointer into the second argument mov -0x1007(%rip), ARG2 # Load the block pointer into the second argument
xchg %rdx, %rsi # Swap the first and second arguments xchg ARG1, ARG2 # Swap the first and second arguments
jmp *-32(%rip) # Call the block function jmp *-0x1008(%rip) # Call the block function
CDECL(__objc_block_trampoline_end_sret): CDECL(__objc_block_trampoline_end_sret):
#elif __i386 #elif __i386
CDECL(__objc_block_trampoline): CDECL(__objc_block_trampoline):
call next_line # Store the instruction pointer on the stack call Lnext_line # Store the instruction pointer on the stack
next_line: Lnext_line:
pop %eax # Load the old instruction pointer pop %eax # Load the old instruction pointer
mov 4(%esp), %ebx # Load the self parameter mov 4(%esp), %ebx # Load the self parameter
mov %ebx, 8(%esp) # Store self as the second argument mov %ebx, 8(%esp) # Store self as the second argument
mov -9(%eax), %ebx # Load the block pointer to %ebx mov -0x1005(%eax), %ebx # Load the block pointer to %ebx
mov %ebx, 4(%esp) # Store the block pointer in the first argument mov %ebx, 4(%esp) # Store the block pointer in the first argument
jmp *-13(%eax) # Call the block function jmp *-0x1001(%eax) # Call the block function
CDECL(__objc_block_trampoline_end): CDECL(__objc_block_trampoline_end):
CDECL(__objc_block_trampoline_sret): CDECL(__objc_block_trampoline_sret):
call next_line2 # Store the instruction pointer on the stack call Lnext_line2 # Store the instruction pointer on the stack
next_line2: Lnext_line2:
pop %eax # Load the old instruction pointer pop %eax # Load the old instruction pointer
mov 8(%esp), %ebx # Load the self parameter mov 8(%esp), %ebx # Load the self parameter
mov %ebx, 12(%esp) # Store self as the second argument mov %ebx, 12(%esp) # Store self as the second argument
mov -9(%eax), %ebx # Load the block pointer to %ebx mov -0x1005(%eax), %ebx # Load the block pointer to %ebx
mov %ebx, 8(%esp) # Store the block pointer in the first argument mov %ebx, 8(%esp) # Store the block pointer in the first argument
jmp *-13(%eax) # Call the block function jmp *-0x1001(%eax) # Call the block function
CDECL(__objc_block_trampoline_end_sret): CDECL(__objc_block_trampoline_end_sret):
#elif __mips__ #elif __mips__
# ifdef _ABI64 # ifdef _ABI64
CDECL(__objc_block_trampoline): CDECL(__objc_block_trampoline):
move $a1, $a0 move $a1, $a0
ld $a0, -16($25) ld $a0, -4096($25)
ld $25, -8($25) ld $25, -4088($25)
jr $25 jr $25
CDECL(__objc_block_trampoline_end): CDECL(__objc_block_trampoline_end):
CDECL(__objc_block_trampoline_sret): CDECL(__objc_block_trampoline_sret):
move $a2, $a1 move $a2, $a1
ld $a1, -16($25) ld $a1, -4096($25)
ld $25, -8($25) ld $25, -4088($25)
jr $25 jr $25
CDECL(__objc_block_trampoline_end_sret): CDECL(__objc_block_trampoline_end_sret):
# else # else
CDECL(__objc_block_trampoline): CDECL(__objc_block_trampoline):
move $a1, $a0 move $a1, $a0
lw $a0, -8($25) lw $a0, -4096($25)
lw $25, -4($25) lw $25, -4092($25)
jr $25 jr $25
CDECL(__objc_block_trampoline_end): CDECL(__objc_block_trampoline_end):
CDECL(__objc_block_trampoline_sret): CDECL(__objc_block_trampoline_sret):
move $a2, $a1 move $a2, $a1
lw $a1, -8($25) lw $a1, -4096($25)
lw $25, -4($25) lw $25, -4092($25)
jr $25 jr $25
CDECL(__objc_block_trampoline_end_sret): CDECL(__objc_block_trampoline_end_sret):
# endif # endif
#elif __arm__ #elif __arm__
CDECL(__objc_block_trampoline): CDECL(__objc_block_trampoline):
sub r12, pc, #4096
mov r1, r0 // Move self over _cmd mov r1, r0 // Move self over _cmd
ldr r0, [pc, #-16] // Load the block pointer over self ldr r0, [r12, #-8] // Load the block pointer over self
ldr pc, [pc, #-24] // Jump to the block function ldr pc, [r12, #-4] // Jump to the block function
CDECL(__objc_block_trampoline_end): CDECL(__objc_block_trampoline_end):
CDECL(__objc_block_trampoline_sret): CDECL(__objc_block_trampoline_sret):
sub r12, pc, #4096
mov r2, r1 // Move self over _cmd mov r2, r1 // Move self over _cmd
ldr r1, [pc, #-16] // Load the block pointer over self ldr r1, [r12, #-8] // Load the block pointer over self
ldr pc, [pc, #-24] // Jump to the block function ldr pc, [r12, #-4] // Jump to the block function
CDECL(__objc_block_trampoline_end_sret): CDECL(__objc_block_trampoline_end_sret):
#else #else
#warning imp_implementationWithBlock() not implemented for your architecture #warning imp_implementationWithBlock() not implemented for your architecture

@ -18,6 +18,24 @@ struct objc_bitfield
int32_t values[0]; int32_t values[0];
}; };
static inline BOOL objc_bitfield_test(uintptr_t bitfield, uint64_t field)
{
if (bitfield & 1)
{
uint64_t bit = 1<<(field+1);
return (bitfield & bit) == bit;
}
struct objc_bitfield *bf = (struct objc_bitfield*)bitfield;
uint64_t byte = field / 32;
if (byte >= bf->length)
{
return NO;
}
uint64_t bit = 1<<(field%32);
return (bf->values[byte] & bit) == bit;
}
struct objc_class struct objc_class
{ {
/** /**
@ -141,13 +159,13 @@ struct objc_class
* bits are set, from low to high, for each ivar in the object that is a * bits are set, from low to high, for each ivar in the object that is a
* strong pointer. * strong pointer.
*/ */
intptr_t strong_pointers; uintptr_t strong_pointers;
/** /**
* The location of all zeroing weak pointer ivars declared by this class. * The location of all zeroing weak pointer ivars declared by this class.
* The format of this field is the same as the format of the * The format of this field is the same as the format of the
* strong_pointers field. * strong_pointers field.
*/ */
intptr_t weak_pointers; uintptr_t weak_pointers;
}; };
/** /**

@ -2,6 +2,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <errno.h>
#include "objc/runtime.h" #include "objc/runtime.h"
#include "objc/hooks.h" #include "objc/hooks.h"
#include "sarray2.h" #include "sarray2.h"
@ -12,7 +13,16 @@
#include "slot_pool.h" #include "slot_pool.h"
#include "dtable.h" #include "dtable.h"
#include "visibility.h" #include "visibility.h"
#include "errno.h" #include "asmconstants.h"
_Static_assert(__builtin_offsetof(struct objc_class, dtable) == DTABLE_OFFSET,
"Incorrect dtable offset for assembly");
_Static_assert(__builtin_offsetof(SparseArray, shift) == SHIFT_OFFSET,
"Incorrect shift offset for assembly");
_Static_assert(__builtin_offsetof(SparseArray, data) == DATA_OFFSET,
"Incorrect data offset for assembly");
_Static_assert(__builtin_offsetof(struct objc_slot, method) == SLOT_OFFSET,
"Incorrect slot offset for assembly");
PRIVATE dtable_t uninstalled_dtable; PRIVATE dtable_t uninstalled_dtable;
#if defined(WITH_TRACING) && defined (__x86_64) #if defined(WITH_TRACING) && defined (__x86_64)
@ -81,6 +91,50 @@ static void checkARCAccessors(Class cls)
objc_set_class_flag(cls, objc_class_flag_fast_arc); objc_set_class_flag(cls, objc_class_flag_fast_arc);
} }
PRIVATE void checkARCAccessorsSlow(Class cls)
{
if (cls->dtable != uninstalled_dtable)
{
return;
}
static SEL retain, release, autorelease, isARC;
if (NULL == retain)
{
retain = sel_registerName("retain");
release = sel_registerName("release");
autorelease = sel_registerName("autorelease");
isARC = sel_registerName("_ARCCompliantRetainRelease");
}
if (cls->super_class != Nil)
{
checkARCAccessorsSlow(cls->super_class);
}
BOOL superIsFast = objc_test_class_flag(cls, objc_class_flag_fast_arc);
BOOL selfImplementsRetainRelease = NO;
for (struct objc_method_list *l=cls->methods ; l != NULL ; l= l->next)
{
for (int i=0 ; i<l->count ; i++)
{
SEL s = l->methods[i].selector;
if (sel_isEqual(s, retain) ||
sel_isEqual(s, release) ||
sel_isEqual(s, autorelease))
{
selfImplementsRetainRelease = YES;
}
else if (sel_isEqual(s, isARC))
{
objc_set_class_flag(cls, objc_class_flag_fast_arc);
return;
}
}
}
if (superIsFast && ! selfImplementsRetainRelease)
{
objc_set_class_flag(cls, objc_class_flag_fast_arc);
}
}
static void collectMethodsForMethodListToSparseArray( static void collectMethodsForMethodListToSparseArray(
struct objc_method_list *list, struct objc_method_list *list,
SparseArray *sarray, SparseArray *sarray,
@ -644,23 +698,36 @@ PRIVATE void objc_resize_dtables(uint32_t newSize)
dtable_depth += 8; dtable_depth += 8;
uint32_t oldMask = uninstalled_dtable->mask; uint32_t oldShift = uninstalled_dtable->shift;
dtable_t old_uninstalled_dtable = uninstalled_dtable;
SparseArrayExpandingArray(uninstalled_dtable, dtable_depth); uninstalled_dtable = SparseArrayExpandingArray(uninstalled_dtable, dtable_depth);
#if defined(WITH_TRACING) && defined (__x86_64) #if defined(WITH_TRACING) && defined (__x86_64)
tracing_dtable = SparseArrayExpandingArray(tracing_dtable, dtable_depth); tracing_dtable = SparseArrayExpandingArray(tracing_dtable, dtable_depth);
#endif #endif
{
LOCK_FOR_SCOPE(&initialize_lock);
for (InitializingDtable *buffer = temporary_dtables ; NULL != buffer ; buffer = buffer->next)
{
buffer->dtable = SparseArrayExpandingArray(buffer->dtable, dtable_depth);
}
}
// Resize all existing dtables // Resize all existing dtables
void *e = NULL; void *e = NULL;
struct objc_class *next; struct objc_class *next;
while ((next = class_table_next(&e))) while ((next = class_table_next(&e)))
{ {
if (next->dtable != (void*)uninstalled_dtable && if (next->dtable == old_uninstalled_dtable)
NULL != next->dtable && {
((SparseArray*)next->dtable)->mask == oldMask) next->dtable = uninstalled_dtable;
next->isa->dtable = uninstalled_dtable;
continue;
}
if (NULL != next->dtable &&
((SparseArray*)next->dtable)->shift == oldShift)
{ {
SparseArrayExpandingArray((void*)next->dtable, dtable_depth); next->dtable = SparseArrayExpandingArray((void*)next->dtable, dtable_depth);
SparseArrayExpandingArray((void*)next->isa->dtable, dtable_depth); next->isa->dtable = SparseArrayExpandingArray((void*)next->isa->dtable, dtable_depth);
} }
} }
} }

@ -126,3 +126,9 @@ void add_method_list_to_class(Class cls,
* Destroys a dtable. * Destroys a dtable.
*/ */
void free_dtable(dtable_t dtable); void free_dtable(dtable_t dtable);
/**
* Checks whether the class supports ARC. This can be used before the dtable
* is installed.
*/
void checkARCAccessorsSlow(Class cls);

@ -258,6 +258,10 @@ static inline struct dwarf_eh_lsda parse_lsda(struct _Unwind_Context *context, u
//lsda.type_table = (uintptr_t*)(data + v); //lsda.type_table = (uintptr_t*)(data + v);
} }
#if defined(__arm__) && !defined(__ARM_DWARF_EH__)
lsda.type_table_encoding = (DW_EH_PE_pcrel | DW_EH_PE_indirect);
#endif
lsda.callsite_encoding = (enum dwarf_data_encoding)(*(data++)); lsda.callsite_encoding = (enum dwarf_data_encoding)(*(data++));
// Action table is immediately after the call site table // Action table is immediately after the call site table

@ -486,8 +486,7 @@ BEGIN_PERSONALITY_FUNCTION(__gnustep_objc_personality_v0)
return internal_objc_personality(version, actions, exceptionClass, return internal_objc_personality(version, actions, exceptionClass,
exceptionObject, context, YES); exceptionObject, context, YES);
} }
// FIXME!
#ifndef __arm__
BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0) BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0)
if (exceptionClass == objc_exception_class) if (exceptionClass == objc_exception_class)
{ {
@ -497,17 +496,15 @@ BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0)
id *newEx = __cxa_allocate_exception(sizeof(id)); id *newEx = __cxa_allocate_exception(sizeof(id));
*newEx = ex->object; *newEx = ex->object;
ex->cxx_exception = objc_init_cxx_exception(newEx); ex->cxx_exception = objc_init_cxx_exception(newEx);
memcpy(ex->cxx_exception, exceptionObject, sizeof(struct _Unwind_Exception));
ex->cxx_exception->exception_class = cxx_exception_class; ex->cxx_exception->exception_class = cxx_exception_class;
ex->cxx_exception->exception_cleanup = cleanup; ex->cxx_exception->exception_cleanup = cleanup;
ex->cxx_exception->private_1 = exceptionObject->private_1;
ex->cxx_exception->private_2 = exceptionObject->private_2;
} }
exceptionObject = ex->cxx_exception; exceptionObject = ex->cxx_exception;
exceptionClass = cxx_exception_class; exceptionClass = cxx_exception_class;
} }
return CALL_PERSONALITY_FUNCTION(__gxx_personality_v0); return CALL_PERSONALITY_FUNCTION(__gxx_personality_v0);
} }
#endif
// Weak references to C++ runtime functions. We don't bother testing that // Weak references to C++ runtime functions. We don't bother testing that
// these are 0 before calling them, because if they are not resolved then we // these are 0 before calling them, because if they are not resolved then we

@ -236,7 +236,7 @@ static const char *sizeof_type(const char *type, size_t *size)
{ {
// All pointers look the same to me. // All pointers look the same to me.
*size += sizeof(void*) * 8; *size += sizeof(void*) * 8;
size_t ignored; size_t ignored = 0;
// Skip the definition of the pointeee type. // Skip the definition of the pointeee type.
return sizeof_type(type+1, &ignored); return sizeof_type(type+1, &ignored);
} }
@ -329,7 +329,7 @@ static const char *alignof_type(const char *type, size_t *align)
{ {
*align = max((alignof(void*) * 8), *align); *align = max((alignof(void*) * 8), *align);
// All pointers look the same to me. // All pointers look the same to me.
size_t ignored; size_t ignored = 0;
// Skip the definition of the pointeee type. // Skip the definition of the pointeee type.
return alignof_type(type+1, &ignored); return alignof_type(type+1, &ignored);
} }

@ -288,7 +288,7 @@ static int PREFIX(_insert)(PREFIX(_table) *table,
return 1; return 1;
} }
/* If this cell is full, try the next one. */ /* If this cell is full, try the next one. */
for (unsigned int i=0 ; i<32 ; i++) for (unsigned int i=1 ; i<33 ; i++)
{ {
PREFIX(_table_cell) second = PREFIX(_table_cell) second =
PREFIX(_table_lookup)(table, hash+i); PREFIX(_table_lookup)(table, hash+i);
@ -374,7 +374,7 @@ static void PREFIX(_table_move_second)(PREFIX(_table) *table,
// Look at each offset defined by the jump table to find the displaced location. // Look at each offset defined by the jump table to find the displaced location.
int hop = __builtin_ffs(jump); int hop = __builtin_ffs(jump);
PREFIX(_table_cell) hopCell = PREFIX(_table_cell) hopCell =
PREFIX(_table_lookup)(table, MAP_TABLE_HASH_VALUE(emptyCell->value) + hop); PREFIX(_table_lookup)(table, (emptyCell - table->table) + hop);
emptyCell->value = hopCell->value; emptyCell->value = hopCell->value;
emptyCell->secondMaps &= ~(1 << (hop-1)); emptyCell->secondMaps &= ~(1 << (hop-1));
if (0 == hopCell->secondMaps) if (0 == hopCell->secondMaps)

@ -1,6 +1,8 @@
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "objc/runtime.h" #include "objc/runtime.h"
#include "objc/objc-arc.h"
#include "class.h" #include "class.h"
#include "ivar.h" #include "ivar.h"
#include "visibility.h" #include "visibility.h"
@ -142,27 +144,105 @@ PRIVATE void objc_compute_ivar_offsets(Class class)
} }
} }
typedef enum {
ownership_invalid,
ownership_strong,
ownership_weak,
ownership_unsafe
} ownership;
ownership ownershipForIvar(Class cls, Ivar ivar)
{
struct objc_ivar_list *list = cls->ivars;
if ((ivar < list->ivar_list) || (ivar >= &list->ivar_list[list->count]))
{
// Try the superclass
if (cls->super_class)
{
return ownershipForIvar(cls->super_class, ivar);
}
return ownership_invalid;
}
if (!objc_test_class_flag(cls, objc_class_flag_new_abi))
{
return ownership_unsafe;
}
if (cls->abi_version < 1)
{
return ownership_unsafe;
}
if (objc_bitfield_test(cls->strong_pointers, (ivar - list->ivar_list)))
{
return ownership_strong;
}
if (objc_bitfield_test(cls->weak_pointers, (ivar - list->ivar_list)))
{
return ownership_weak;
}
return ownership_unsafe;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Public API functions // Public API functions
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void object_setIvar(id object, Ivar ivar, id value) void object_setIvar(id object, Ivar ivar, id value)
{ {
char *addr = (char*)object; ownershipForIvar(object_getClass(object), ivar);
addr += ivar_getOffset(ivar); id *addr = (id*)((char*)object + ivar_getOffset(ivar));
*(id*)addr = value; switch (ownershipForIvar(object_getClass(object), ivar))
{
case ownership_strong:
objc_storeStrong(addr, value);
break;
case ownership_weak:
objc_storeWeak(addr, value);
break;
case ownership_unsafe:
*addr = value;
break;
case ownership_invalid:
#ifndef NDEBUG
fprintf(stderr, "Ivar does not belong to this class!\n");
#endif
break;
}
} }
Ivar object_setInstanceVariable(id obj, const char *name, void *value) Ivar object_setInstanceVariable(id obj, const char *name, void *value)
{ {
Ivar ivar = class_getInstanceVariable(object_getClass(obj), name); Ivar ivar = class_getInstanceVariable(object_getClass(obj), name);
object_setIvar(obj, ivar, value); if (ivar_getTypeEncoding(ivar)[0] == '@')
{
object_setIvar(obj, ivar, *(id*)value);
}
else
{
size_t size = objc_sizeof_type(ivar_getTypeEncoding(ivar));
memcpy((char*)obj + ivar_getOffset(ivar), value, size);
}
return ivar; return ivar;
} }
id object_getIvar(id object, Ivar ivar) id object_getIvar(id object, Ivar ivar)
{ {
return *(id*)(((char*)object) + ivar_getOffset(ivar)); ownershipForIvar(object_getClass(object), ivar);
id *addr = (id*)((char*)object + ivar_getOffset(ivar));
switch (ownershipForIvar(object_getClass(object), ivar))
{
case ownership_strong:
return objc_retainAutoreleaseReturnValue(*addr);
case ownership_weak:
return objc_loadWeak(addr);
break;
case ownership_unsafe:
return *addr;
case ownership_invalid:
#ifndef NDEBUG
fprintf(stderr, "Ivar does not belong to this class!\n");
#endif
return nil;
}
} }
Ivar object_getInstanceVariable(id obj, const char *name, void **outValue) Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)
@ -170,7 +250,7 @@ Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)
Ivar ivar = class_getInstanceVariable(object_getClass(obj), name); Ivar ivar = class_getInstanceVariable(object_getClass(obj), name);
if (NULL != outValue) if (NULL != outValue)
{ {
*outValue = object_getIvar(obj, ivar); *outValue = (((char*)obj) + ivar_getOffset(ivar));
} }
return ivar; return ivar;
} }

@ -1,4 +1,5 @@
#include "common.S" #include "common.S"
#include "asmconstants.h"
#if __x86_64 #if __x86_64
#include "objc_msgSend.x86-64.S" #include "objc_msgSend.x86-64.S"
#elif __i386 #elif __i386

@ -1,10 +1,28 @@
#define DTABLE_OFFSET 32
#define SMALLOBJ_MASK 1
#define SHIFT_OFFSET 4
#define DATA_OFFSET 12
#define SLOT_OFFSET 16
.syntax unified .syntax unified
.fpu neon .fpu neon
#if ((__ARM_ARCH >= 7) || defined (__ARM_ARCH_6T2__))
.macro byte1 dst, src
uxtb \dst, \src
.endm
.macro byte2 dst, src
ubfx \dst, \src, #8, #8
.endm
.macro byte3 dst, src
ubfx \dst, \src, #16, #8
.endm
#else
.macro byte1 dst, src
and \dst, \src, #0xff
.endm
.macro byte2 dst, src
and \dst, \src, #0xff00
lsr \dst, \dst, 8
.endm
.macro byte3 dst, src
and \dst, \src, #0xff00
lsr \dst, \dst, 16
.endm
#endif
// Macro for testing: logs a register value to standard error // Macro for testing: logs a register value to standard error
.macro LOG reg .macro LOG reg
@ -37,23 +55,23 @@
ldr r5, [\sel] // selector->index -> r5 ldr r5, [\sel] // selector->index -> r5
ldr r6, [r4, #SHIFT_OFFSET] // dtable->shift -> r6 ldr r6, [r4, #SHIFT_OFFSET] // dtable->shift -> r6
ldr r4, [r4, #DATA_OFFSET] // dtable->data -> r4
teq r6, #8 // If this is a small dtable, jump to the small dtable handlers teq r6, #8 // If this is a small dtable, jump to the small dtable handlers
beq 1f beq 1f
teq r6, #0 teq r6, #0
beq 2f beq 2f
and r6, r5, #0xff0000 byte3 r6, r5 // Put byte 3 of the sel id in r6
ldr r4, [r4, r6, asr#14] add r6, r4, r6, lsl #2 // r6 = dtable address + dtable data offset
ldr r4, [r4, #DATA_OFFSET] ldr r4, [r6, #DATA_OFFSET] // Load, adding in the data offset
1: // dtable16 1: // dtable16
and r6, r5, #0xff00 byte2 r6, r5 // Put byte 2 of the sel id in r6
ldr r4, [r4, r6, asr#6] add r6, r4, r6, lsl #2 // r6 = dtable address + dtable data offset
ldr r4, [r4, #DATA_OFFSET] ldr r4, [r6, #DATA_OFFSET] // Load, adding in the data offset
2: // dtable8 2: // dtable8
and r6, r5, #0xff byte1 r6, r5 // Low byte of sel id into r5
ldr ip, [r4, r6, asl#2] add r6, r4, r6, lsl #2 // r6 = dtable address + dtable data offset
ldr ip, [r6, #DATA_OFFSET] // Load, adding in the data offset
teq ip, #0 // If the slot is nil teq ip, #0 // If the slot is nil
beq 5f // Go to the slow path and do the forwarding stuff beq 5f // Go to the slow path and do the forwarding stuff
@ -71,10 +89,14 @@
5: // Slow lookup 5: // Slow lookup
push {r0-r4, lr} // Save anything that will be clobbered by the call push {r0-r4, lr} // Save anything that will be clobbered by the call
.save {r0-r4, lr} .save {r0-r4, lr}
#ifndef __SOFTFP__
vpush {q0-q3}
.pad #64
#endif
push {\receiver} // &self, _cmd in arguments push {\receiver} // &self, _cmd in arguments
.save {\receiver} .save {\receiver}
mov r0, sp mov r0, sp
mov r1, \sel mov r1, \sel
@ -82,6 +104,9 @@
mov ip, r0 // IMP -> ip mov ip, r0 // IMP -> ip
pop {r5} // restore (modified) self to r5 pop {r5} // restore (modified) self to r5
#ifndef __SOFTFP__
vpop {q0-q3}
#endif
pop {r0-r4, lr} // Load clobbered registers pop {r0-r4, lr} // Load clobbered registers
mov \receiver, r5 mov \receiver, r5
b 3b b 3b

@ -3,20 +3,10 @@
#ifdef _ABI64 #ifdef _ABI64
#define LP ld #define LP ld
#define SP sd #define SP sd
#define DTABLE_OFFSET 64
#define SMALLOBJ_MASK 7
#define SHIFT_OFFSET 4
#define DATA_OFFSET 16
#define SLOT_OFFSET 32
#else #else
#warning N32 is untested, O32 is unsupported. #warning N32 is untested, O32 is unsupported.
#define LP lw #define LP lw
#define SP sw #define SP sw
#define DTABLE_OFFSET 32
#define SMALLOBJ_MASK 1
#define SHIFT_OFFSET 4
#define DATA_OFFSET 12
#define SLOT_OFFSET 16
#endif #endif
.macro dump_and_crash reg .macro dump_and_crash reg
@ -51,62 +41,62 @@ lw $zero, ($zero)
daddiu $t8, $t8, %lo(%neg(%gp_rel(0b))) daddiu $t8, $t8, %lo(%neg(%gp_rel(0b)))
andi $t4, \receiver, SMALLOBJ_MASK # Check if the receiver is a small object andi $t0, \receiver, SMALLOBJ_MASK # Check if the receiver is a small object
bne $t4, $0, 6f # Get the small object class bne $t0, $0, 6f # Get the small object class
nop nop
LP $t5, (\sel) LP $t1, (\sel)
# By this point, we have a non-nil # By this point, we have a non-nil
# receiver that is a real pointer # receiver that is a real pointer
LP $t4, (\receiver) # Load the class LP $t0, (\receiver) # Load the class
1: # class loaded, stored in $t4 1: # class loaded, stored in $t0
LP $t4, DTABLE_OFFSET($t4) # Load the dtable from the class LP $t0, DTABLE_OFFSET($t0) # Load the dtable from the class
lw $t6, SHIFT_OFFSET($t4) # Load the shift (dtable size) lw $t2, SHIFT_OFFSET($t0) # Load the shift (dtable size)
# $t4 = dtable, $t5 = sel index # $t0 = dtable, $t1 = sel index
LP $t7, DATA_OFFSET($t4) # Load the address of the start of the array daddi $t3, $t0, DATA_OFFSET # Compute the address of the start of the array
beq $0, $t6, 3f # If this is a small dtable, jump to the small dtable handlers beq $0, $t2, 3f # If this is a small dtable, jump to the small dtable handlers
daddi $v0, $t6, -8 daddi $v0, $t2, -8
beq $0, $v0, 2f beq $0, $v0, 2f
lui $t6, 0x00ff # The mask for a big dtable won't fit in an and immediate lui $t2, 0x00ff # The mask for a big dtable won't fit in an and immediate
and $t6, $t6, $t5 # mask the selector and $t2, $t2, $t1 # mask the selector
#ifdef _ABI64 #ifdef _ABI64
dsrl $t6, $t6, 13 # Right shift 16, but then left shift by pointer size dsrl $t2, $t2, 13 # Right shift 16, but then left shift by pointer size
#else #else
srl $t6, $t6, 14 srl $t2, $t2, 14
#endif #endif
dadd $t6, $t6, $t7 dadd $t2, $t2, $t3
LP $t7, ($t6) LP $t3, ($t2)
LP $t7, DATA_OFFSET($t7) daddi $t3, $t3, DATA_OFFSET # Compute the address of the start of the array
2: # dtable16: 2: # dtable16:
andi $t6, $t5, 0xff00 # mask the selector andi $t2, $t1, 0xff00 # mask the selector
#ifdef _ABI64 #ifdef _ABI64
dsrl $t6, $t6, 5 # Right shift 8, but then left shift by pointer size dsrl $t2, $t2, 5 # Right shift 8, but then left shift by pointer size
#else #else
srl $t6, $t6, 6 srl $t2, $t2, 6
#endif #endif
dadd $t6, $t6, $t7 dadd $t2, $t2, $t3
LP $t7, ($t6) LP $t3, ($t2)
LP $t7, DATA_OFFSET($t7) daddi $t3, $t3, DATA_OFFSET # Compute the address of the start of the array
3: # dtable8: 3: # dtable8:
andi $t6, $t5, 0xff # mask the selector andi $t2, $t1, 0xff # mask the selector
#ifdef _ABI64 #ifdef _ABI64
dsll $t6, $t6, 3 # Left shift by pointer size dsll $t2, $t2, 3 # Left shift by pointer size
#else #else
sll $t6, $t6, 2 sll $t2, $t2, 2
#endif #endif
dadd $t6, $t6, $t7 dadd $t2, $t2, $t3
LP $t7, ($t6) LP $t3, ($t2)
beq $0, $t7, 5f # Nil slot - invoke some kind of forwarding mechanism beq $0, $t3, 5f # Nil slot - invoke some kind of forwarding mechanism
nop nop
LP $25, SLOT_OFFSET($t7) LP $25, SLOT_OFFSET($t3)
jr $25 jr $25
nop nop
4: # returnNil: 4: # returnNil:
@ -163,7 +153,8 @@ lw $zero, ($zero)
daddiu $a1, $a2, 0 # replace self with _cmd in $a1 daddiu $a1, $a2, 0 # replace self with _cmd in $a1
.endif .endif
.cfi_adjust_cfa_offset -SAVE_SIZE .cfi_def_cfa_offset SAVE_SIZE
.cfi_offset 31, (64 - SAVE_SIZE)
jalr $25 # Call the slow lookup function jalr $25 # Call the slow lookup function
nop nop
@ -192,14 +183,14 @@ lw $zero, ($zero)
daddiu $sp, $sp, SAVE_SIZE daddiu $sp, $sp, SAVE_SIZE
6: # smallObject: 6: # smallObject:
#if _ABI64 #if _ABI64
dsll $t4, $t4, 3 # Convert tag to pointer offset dsll $t0, $t0, 3 # Convert tag to pointer offset
LP $t6, %got_disp(CDECL(SmallObjectClasses))($t8) # Load small object classes array address LP $t2, %got_disp(CDECL(SmallObjectClasses))($t8) # Load small object classes array address
daddu $t4, $t4, $t6 # Add the base address to the offset daddu $t0, $t0, $t2 # Add the base address to the offset
b 1b # Return to the normal path b 1b # Return to the normal path
LP $t4, ($t4) # Load the class (in delay slot) LP $t0, ($t0) # Load the class (in delay slot)
#else #else
b 1b b 1b
LP $t4, %got_disp(CDECL(SmallIntClass))($t8) LP $t0, %got_disp(CDECL(SmallIntClass))($t8)
#endif #endif
.cfi_endproc .cfi_endproc
.endm .endm

@ -1,8 +1,3 @@
#define DTABLE_OFFSET 32
#define SMALLOBJ_MASK 1
#define SHIFT_OFFSET 4
#define DATA_OFFSET 12
#define SLOT_OFFSET 16
.macro MSGSEND receiver, sel, fpret .macro MSGSEND receiver, sel, fpret
.cfi_startproc .cfi_startproc
movl \receiver(%esp), %eax movl \receiver(%esp), %eax
@ -24,30 +19,20 @@
# %edx: selector index fragment # %edx: selector index fragment
mov SHIFT_OFFSET(%eax), %edx # Load the shift (dtable size) mov SHIFT_OFFSET(%eax), %edx # Load the shift (dtable size)
mov DATA_OFFSET(%eax), %eax # load the address of the start of the array
cmpl $8, %edx # If this is a small dtable, jump to the small dtable handlers cmpl $8, %edx # If this is a small dtable, jump to the small dtable handlers
je 2f je 2f
cmpl $0, %edx cmpl $0, %edx
je 3f je 3f
mov %ecx, %edx mov %ecx, %edx
and $0xff0000, %edx shrl $16, %edx
shrl $14, %edx # Right shift 16, but then left shift by 2 (* sizeof(void*)) movl DATA_OFFSET(%eax, %edx, 4), %eax
add %edx, %eax
mov (%eax), %eax
mov DATA_OFFSET(%eax), %eax
2: # dtable16: 2: # dtable16:
mov %ecx, %edx movzbl %ch, %edx
and $0xff00, %edx movl DATA_OFFSET(%eax, %edx, 4), %eax
shrl $6, %edx
add %edx, %eax
mov (%eax), %eax
mov DATA_OFFSET(%eax), %eax
3: # dtable8: 3: # dtable8:
and $0xff, %ecx movzbl %cl, %edx
shll $2, %ecx movl DATA_OFFSET(%eax, %edx, 4), %eax
add %ecx, %eax
mov (%eax), %eax
test %eax, %eax test %eax, %eax
jz 5f # Nil slot - invoke some kind of forwarding mechanism jz 5f # Nil slot - invoke some kind of forwarding mechanism

@ -1,8 +1,3 @@
#define DTABLE_OFFSET 64
#define SMALLOBJ_MASK 7
#define SHIFT_OFFSET 4
#define DATA_OFFSET 16
#define SLOT_OFFSET 32
.macro MSGSEND receiver, sel .macro MSGSEND receiver, sel
.cfi_startproc # Start emitting unwind data. We .cfi_startproc # Start emitting unwind data. We
@ -18,41 +13,28 @@
jnz 6f # Get the small object class jnz 6f # Get the small object class
mov (\receiver), %r10 # Load the dtable from the class mov (\receiver), %r10 # Load the dtable from the class
1: # classLoaded 1: # classLoaded
mov DTABLE_OFFSET(%r10), %r10 # Load the dtable from the class mov DTABLE_OFFSET(%r10), %r10 # Load the dtable from the class into r10
mov %rax, -8(%rsp) # %rax contains information for variadic calls
push %r12 mov %rbx, -16(%rsp) # On the fast path, spill into the red zone
push %r13 mov (\sel), %eax # Load the selector index into %eax
mov SHIFT_OFFSET(%r10), %r11d # Load the shift (dtable size) into r11
mov (\sel), %r11 # Load the selector index cmpl $8, %r11d # If this is a small dtable, jump to the small dtable handlers
mov SHIFT_OFFSET(%r10), %r13 # Load the shift (dtable size)
mov DATA_OFFSET(%r10), %r12 # load the address of the start of the array
cmpl $8, %r13d # If this is a small dtable, jump to the small dtable handlers
je 2f je 2f
cmpl $0, %r13d cmpl $0, %r11d
je 3f je 3f
mov %r11, %r13 movl %eax, %r11d
and $0xff0000, %r13 shrl $16, %r11d
shrl $13, %r13d # Right shift 16, but then left shift by 3 *sizeof(void*) movq DATA_OFFSET(%r10, %r11, 8), %r10
add %r13, %r12
mov (%r12), %r12
mov DATA_OFFSET(%r12), %r12
2: # dtable16: 2: # dtable16:
mov %r11, %r13 movzbl %ah, %ebx
and $0xff00, %r13 movq DATA_OFFSET(%r10, %rbx, 8), %r10
shrl $5, %r13d
add %r13, %r12
mov (%r12), %r12
mov DATA_OFFSET(%r12), %r12
3: # dtable8: 3: # dtable8:
mov %r11, %r13 movzbl %al, %ebx
and $0xff, %r13 mov -8(%rsp), %rax
shll $3, %r13d movq DATA_OFFSET(%r10, %rbx, 8), %r10
add %r13, %r12 mov -16(%rsp), %rbx
mov (%r12), %r10
pop %r13
pop %r12
test %r10, %r10 test %r10, %r10
jz 5f # Nil slot - invoke some kind of forwarding mechanism jz 5f # Nil slot - invoke some kind of forwarding mechanism
mov SLOT_OFFSET(%r10), %r10 mov SLOT_OFFSET(%r10), %r10
@ -259,10 +241,8 @@
jmp 7b jmp 7b
6: # smallObject: 6: # smallObject:
and \receiver, %r10 # Find the small int type and \receiver, %r10 # Find the small int type
shll $3, %r10d
lea CDECL(SmallObjectClasses)(%rip), %r11 lea CDECL(SmallObjectClasses)(%rip), %r11
add %r11, %r10 mov (%r11, %r10, 8), %r10
mov (%r10), %r10
jmp 1b jmp 1b
.cfi_endproc .cfi_endproc
.endm .endm

@ -45,6 +45,17 @@ struct objc_protocol2 *protocol_for_name(const char *name)
static id ObjC2ProtocolClass = 0; static id ObjC2ProtocolClass = 0;
static id incompleteProtocolClass(void)
{
static id IncompleteProtocolClass = 0;
if (IncompleteProtocolClass == nil)
{
IncompleteProtocolClass = objc_getClass("__IncompleteProtocol");
}
return IncompleteProtocolClass;
}
static int isEmptyProtocol(struct objc_protocol2 *aProto) static int isEmptyProtocol(struct objc_protocol2 *aProto)
{ {
int isEmpty = int isEmpty =
@ -497,7 +508,7 @@ Protocol*__unsafe_unretained* objc_copyProtocolList(unsigned int *outCount)
Protocol *objc_allocateProtocol(const char *name) Protocol *objc_allocateProtocol(const char *name)
{ {
if (objc_getProtocol(name) != NULL) { return NULL; } if (objc_getProtocol(name) != NULL) { return NULL; }
Protocol *p = calloc(1, sizeof(Protocol2)); Protocol *p = (Protocol*)class_createInstance((Class)incompleteProtocolClass(), 0);
p->name = strdup(name); p->name = strdup(name);
return p; return p;
} }
@ -506,7 +517,7 @@ void objc_registerProtocol(Protocol *proto)
if (NULL == proto) { return; } if (NULL == proto) { return; }
LOCK_RUNTIME_FOR_SCOPE(); LOCK_RUNTIME_FOR_SCOPE();
if (objc_getProtocol(proto->name) != NULL) { return; } if (objc_getProtocol(proto->name) != NULL) { return; }
if (nil != proto->isa) { return; } if (incompleteProtocolClass() != proto->isa) { return; }
proto->isa = ObjC2ProtocolClass; proto->isa = ObjC2ProtocolClass;
protocol_table_insert((struct objc_protocol2*)proto); protocol_table_insert((struct objc_protocol2*)proto);
} }
@ -517,7 +528,7 @@ void protocol_addMethodDescription(Protocol *aProtocol,
BOOL isInstanceMethod) BOOL isInstanceMethod)
{ {
if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; } if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; }
if (nil != aProtocol->isa) { return; } if (incompleteProtocolClass() != aProtocol->isa) { return; }
Protocol2 *proto = (Protocol2*)aProtocol; Protocol2 *proto = (Protocol2*)aProtocol;
struct objc_method_description_list **listPtr; struct objc_method_description_list **listPtr;
if (isInstanceMethod) if (isInstanceMethod)
@ -561,6 +572,7 @@ void protocol_addMethodDescription(Protocol *aProtocol,
void protocol_addProtocol(Protocol *aProtocol, Protocol *addition) void protocol_addProtocol(Protocol *aProtocol, Protocol *addition)
{ {
if ((NULL == aProtocol) || (NULL == addition)) { return; } if ((NULL == aProtocol) || (NULL == addition)) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; }
Protocol2 *proto = (Protocol2*)aProtocol; Protocol2 *proto = (Protocol2*)aProtocol;
if (NULL == proto->protocol_list) if (NULL == proto->protocol_list)
{ {
@ -584,7 +596,7 @@ void protocol_addProperty(Protocol *aProtocol,
BOOL isInstanceProperty) BOOL isInstanceProperty)
{ {
if ((NULL == aProtocol) || (NULL == name)) { return; } if ((NULL == aProtocol) || (NULL == name)) { return; }
if (nil != aProtocol->isa) { return; } if (incompleteProtocolClass() != aProtocol->isa) { return; }
if (!isInstanceProperty) { return; } if (!isInstanceProperty) { return; }
Protocol2 *proto = (Protocol2*)aProtocol; Protocol2 *proto = (Protocol2*)aProtocol;
struct objc_property_list **listPtr; struct objc_property_list **listPtr;

@ -348,6 +348,7 @@ id class_createInstance(Class cls, size_t extraBytes)
if (Nil == cls) { return nil; } if (Nil == cls) { return nil; }
id obj = gc->allocate_class(cls, extraBytes); id obj = gc->allocate_class(cls, extraBytes);
obj->isa = cls; obj->isa = cls;
checkARCAccessorsSlow(cls);
call_cxx_construct(obj); call_cxx_construct(obj);
return obj; return obj;
} }

@ -6,17 +6,12 @@
#include "sarray2.h" #include "sarray2.h"
#include "visibility.h" #include "visibility.h"
static void *EmptyArrayData[256]; const static SparseArray EmptyArray = { 0, 0, .data[0 ... 255] = 0 };
static SparseArray EmptyArray = { 0xff, 0, 0, (void**)&EmptyArrayData}; const static SparseArray EmptyArray8 = { 8, 0, .data[0 ... 255] = (void*)&EmptyArray};
static void *EmptyArrayData8[256] = { [0 ... 255] = &EmptyArray }; const static SparseArray EmptyArray16 = { 16, 0, .data[0 ... 255] = (void*)&EmptyArray8};
static SparseArray EmptyArray8 = { 0xff00, 8, 0, (void**)&EmptyArrayData8}; const static SparseArray EmptyArray24 = { 24, 0, .data[0 ... 255] = (void*)&EmptyArray16};
static void *EmptyArrayData16[256] = { [0 ... 255] = &EmptyArray8 };
static SparseArray EmptyArray16 = { 0xff0000, 16, 0, (void**)&EmptyArrayData16};
static void *EmptyArrayData24[256] = { [0 ... 255] = &EmptyArray16 };
static SparseArray EmptyArray24 = { 0xff0000, 24, 0, (void**)&EmptyArrayData24};
#define MAX_INDEX(sarray) (sarray->mask >> sarray->shift) #define MAX_INDEX(sarray) (0xff)
#define DATA_SIZE(sarray) ((sarray->mask >> sarray->shift) + 1)
// Tweak this value to trade speed for memory usage. Bigger values use more // Tweak this value to trade speed for memory usage. Bigger values use more
// memory, but give faster lookups. // memory, but give faster lookups.
@ -29,19 +24,18 @@ void *EmptyChildForShift(uint32_t shift)
{ {
default: UNREACHABLE("Broken sparse array"); default: UNREACHABLE("Broken sparse array");
case 8: case 8:
return &EmptyArray; return (void*)&EmptyArray;
case 16: case 16:
return &EmptyArray8; return (void*)&EmptyArray8;
case 24: case 24:
return &EmptyArray16; return (void*)&EmptyArray16;
case 32: case 32:
return &EmptyArray24; return (void*)&EmptyArray24;
} }
} }
static void init_pointers(SparseArray * sarray) static void init_pointers(SparseArray * sarray)
{ {
sarray->data = calloc(DATA_SIZE(sarray), sizeof(void*));
if(sarray->shift != 0) if(sarray->shift != 0)
{ {
void *data = EmptyChildForShift(sarray->shift); void *data = EmptyChildForShift(sarray->shift);
@ -57,7 +51,6 @@ PRIVATE SparseArray * SparseArrayNewWithDepth(uint32_t depth)
SparseArray * sarray = calloc(1, sizeof(SparseArray)); SparseArray * sarray = calloc(1, sizeof(SparseArray));
sarray->refCount = 1; sarray->refCount = 1;
sarray->shift = depth-base_shift; sarray->shift = depth-base_shift;
sarray->mask = base_mask << sarray->shift;
init_pointers(sarray); init_pointers(sarray);
return sarray; return sarray;
} }
@ -77,24 +70,17 @@ PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new
assert(sarray->refCount == 1); assert(sarray->refCount == 1);
SparseArray *new = calloc(1, sizeof(SparseArray)); SparseArray *new = calloc(1, sizeof(SparseArray));
new->refCount = 1; new->refCount = 1;
new->shift = sarray->shift; new->shift = sarray->shift + 8;
new->mask = sarray->mask; new->data[0] = sarray;
void **newData = malloc(DATA_SIZE(sarray) * sizeof(void*)); void *data = EmptyChildForShift(new->shift);
void *data = EmptyChildForShift(new->shift + 8);
for(unsigned i=1 ; i<=MAX_INDEX(sarray) ; i++) for(unsigned i=1 ; i<=MAX_INDEX(sarray) ; i++)
{ {
newData[i] = data; new->data[i] = data;
} }
new->data = sarray->data;
newData[0] = new;
sarray->data = newData;
// Now, any lookup in sarray for any value less than its capacity will have // Now, any lookup in sarray for any value less than its capacity will have
// all non-zero values shifted away, resulting in 0. All lookups will // all non-zero values shifted away, resulting in 0. All lookups will
// therefore go to the new sarray. // therefore go to the new sarray.
sarray->shift += base_shift; return new;
// Finally, set the mask to the correct value. Now all lookups should work.
sarray->mask <<= base_shift;
return sarray;
} }
static void *SparseArrayFind(SparseArray * sarray, uint32_t * index) static void *SparseArrayFind(SparseArray * sarray, uint32_t * index)
@ -117,7 +103,7 @@ static void *SparseArrayFind(SparseArray * sarray, uint32_t * index)
{ {
// If the shift is not 0, then we need to recursively look at child // If the shift is not 0, then we need to recursively look at child
// nodes. // nodes.
uint32_t zeromask = ~(sarray->mask >> base_shift); uint32_t zeromask = ~((0xff << sarray->shift) >> base_shift);
while (j<max) while (j<max)
{ {
//Look in child nodes //Look in child nodes
@ -178,7 +164,6 @@ PRIVATE void SparseArrayInsert(SparseArray * sarray, uint32_t index, void *value
{ {
newsarray->shift = sarray->shift - base_shift; newsarray->shift = sarray->shift - base_shift;
} }
newsarray->mask = sarray->mask >> base_shift;
init_pointers(newsarray); init_pointers(newsarray);
sarray->data[i] = newsarray; sarray->data[i] = newsarray;
child = newsarray; child = newsarray;
@ -194,25 +179,28 @@ PRIVATE void SparseArrayInsert(SparseArray * sarray, uint32_t index, void *value
} }
else else
{ {
sarray->data[index & sarray->mask] = value; sarray->data[MASK_INDEX(index)] = value;
} }
} }
PRIVATE SparseArray *SparseArrayCopy(SparseArray * sarray) PRIVATE SparseArray *SparseArrayCopy(SparseArray * sarray)
{ {
SparseArray *copy = calloc(1, sizeof(SparseArray)); SparseArray *copy = calloc(sizeof(SparseArray), 1);
memcpy(copy, sarray, sizeof(SparseArray));
copy->refCount = 1; copy->refCount = 1;
copy->shift = sarray->shift;
copy->mask = sarray->mask;
copy->data = malloc(sizeof(void*) * DATA_SIZE(sarray));
memcpy(copy->data, sarray->data, sizeof(void*) * DATA_SIZE(sarray));
// If the sarray has children, increase their refcounts and link them // If the sarray has children, increase their refcounts and link them
if (sarray->shift > 0) if (sarray->shift > 0)
{ {
for (unsigned int i = 0 ; i<=MAX_INDEX(sarray); i++) for (unsigned int i = 0 ; i<=MAX_INDEX(sarray); i++)
{ {
SparseArray *child = copy->data[i]; SparseArray *child = copy->data[i];
__sync_fetch_and_add(&child->refCount, 1); if (!(child == &EmptyArray ||
child == &EmptyArray8 ||
child == &EmptyArray16 ||
child == &EmptyArray24))
{
__sync_fetch_and_add(&child->refCount, 1);
}
// Non-lazy copy. Uncomment if debugging // Non-lazy copy. Uncomment if debugging
// copy->data[i] = SparseArrayCopy(copy->data[i]); // copy->data[i] = SparseArrayCopy(copy->data[i]);
} }
@ -223,9 +211,10 @@ PRIVATE SparseArray *SparseArrayCopy(SparseArray * sarray)
PRIVATE void SparseArrayDestroy(SparseArray * sarray) PRIVATE void SparseArrayDestroy(SparseArray * sarray)
{ {
// Don't really delete this sarray if its ref count is > 0 // Don't really delete this sarray if its ref count is > 0
if (sarray == &EmptyArray || if (sarray == &EmptyArray ||
sarray == &EmptyArray8 || sarray == &EmptyArray8 ||
sarray == &EmptyArray16 || sarray == &EmptyArray16 ||
sarray == &EmptyArray24 ||
(__sync_sub_and_fetch(&sarray->refCount, 1) > 0)) (__sync_sub_and_fetch(&sarray->refCount, 1) > 0))
{ {
return; return;
@ -233,13 +222,11 @@ PRIVATE void SparseArrayDestroy(SparseArray * sarray)
if(sarray->shift > 0) if(sarray->shift > 0)
{ {
uint32_t max = (sarray->mask >> sarray->shift) + 1; for(uint32_t i=0 ; i<data_size ; i++)
for(uint32_t i=0 ; i<max ; i++)
{ {
SparseArrayDestroy((SparseArray*)sarray->data[i]); SparseArrayDestroy((SparseArray*)sarray->data[i]);
} }
} }
free(sarray->data);
free(sarray); free(sarray);
} }

@ -13,6 +13,17 @@
#include <stdlib.h> #include <stdlib.h>
#include "visibility.h" #include "visibility.h"
/**
* The size of the data array. The sparse array is a tree with this many
* children at each node depth.
*/
static const uint32_t data_size = 256;
/**
* The mask used to access the elements in the data array in a sparse array
* node.
*/
static const uint32_t data_mask = data_size - 1;
/** /**
* Sparse arrays, used to implement dispatch tables. Current implementation is * Sparse arrays, used to implement dispatch tables. Current implementation is
* quite RAM-intensive and could be optimised. Maps 32-bit integers to pointers. * quite RAM-intensive and could be optimised. Maps 32-bit integers to pointers.
@ -25,11 +36,6 @@
*/ */
typedef struct typedef struct
{ {
/**
* Mask value applied to the index when generating an index in this
* sub-array.
*/
uint32_t mask;
/** /**
* Number of bits that the masked value should be right shifted by to get * Number of bits that the masked value should be right shifted by to get
* the index in the subarray. If this value is greater than zero, then the * the index in the subarray. If this value is greater than zero, then the
@ -46,14 +52,14 @@ typedef struct
/** /**
* The data stored in this sparse array node. * The data stored in this sparse array node.
*/ */
void ** data; void *data[data_size];
} SparseArray; } SparseArray;
/** /**
* Turn an index in the array into an index in the current depth. * Turn an index in the array into an index in the current depth.
*/ */
#define MASK_INDEX(index) \ #define MASK_INDEX(index) \
((index & sarray->mask) >> sarray->shift) ((index >> sarray->shift) & 0xff)
#define SARRAY_EMPTY ((void*)0) #define SARRAY_EMPTY ((void*)0)
/** /**
@ -64,7 +70,7 @@ typedef struct
static inline void* SparseArrayLookup(SparseArray * sarray, uint32_t index) static inline void* SparseArrayLookup(SparseArray * sarray, uint32_t index)
{ {
// This unrolled version of the commented-out segment below only works with // This unrolled version of the commented-out segment below only works with
// sarrays that use one-byte leaves. It's really ugly, but seems to be faster. // sarrays that use one-byte leafs. It's really ugly, but seems to be faster.
// With this version, we get the same performance as the old GNU code, but // With this version, we get the same performance as the old GNU code, but
// with about half the memory usage. // with about half the memory usage.
uint32_t i = index; uint32_t i = index;

@ -9,7 +9,6 @@
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include "lock.h" #include "lock.h"
#include "sarray2.h"
#include "objc/runtime.h" #include "objc/runtime.h"
#include "method_list.h" #include "method_list.h"
#include "class.h" #include "class.h"
@ -37,10 +36,14 @@
* array. * array.
*/ */
static uint32_t selector_count = 1; static uint32_t selector_count = 1;
/**
* Size of the buffer.
*/
static size_t table_size;
/** /**
* Mapping from selector numbers to selector names. * Mapping from selector numbers to selector names.
*/ */
PRIVATE SparseArray *selector_list = NULL; PRIVATE struct sel_type_list **selector_list = NULL;
#ifdef DEBUG_SELECTOR_TABLE #ifdef DEBUG_SELECTOR_TABLE
#define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__) #define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
@ -51,6 +54,27 @@ PRIVATE SparseArray *selector_list = NULL;
// Get the functions for string hashing // Get the functions for string hashing
#include "string_hash.h" #include "string_hash.h"
/**
* Lock protecting the selector table.
*/
mutex_t selector_table_lock;
static inline struct sel_type_list *selLookup_locked(uint32_t idx)
{
if (idx > selector_count)
{
return NULL;
}
return selector_list[idx];
}
static inline struct sel_type_list *selLookup(uint32_t idx)
{
LOCK_FOR_SCOPE(&selector_table_lock);
return selLookup_locked(idx);
}
PRIVATE inline BOOL isSelRegistered(SEL sel) PRIVATE inline BOOL isSelRegistered(SEL sel)
{ {
if ((uintptr_t)sel->name < (uintptr_t)selector_count) if ((uintptr_t)sel->name < (uintptr_t)selector_count)
@ -65,8 +89,7 @@ static const char *sel_getNameNonUnique(SEL sel)
const char *name = sel->name; const char *name = sel->name;
if (isSelRegistered(sel)) if (isSelRegistered(sel))
{ {
struct sel_type_list * list = struct sel_type_list * list = selLookup_locked(sel->index);
SparseArrayLookup(selector_list, sel->index);
name = (list == NULL) ? NULL : list->value; name = (list == NULL) ? NULL : list->value;
} }
if (NULL == name) if (NULL == name)
@ -226,16 +249,11 @@ static inline uint32_t hash_selector(const void *s)
*/ */
static selector_table *sel_table; static selector_table *sel_table;
/**
* Lock protecting the selector table.
*/
mutex_t selector_table_lock;
static int selector_name_copies; static int selector_name_copies;
PRIVATE void log_selector_memory_usage(void) PRIVATE void log_selector_memory_usage(void)
{ {
fprintf(stderr, "%d bytes in selector name list.\n", SparseArraySize(selector_list)); fprintf(stderr, "%lu bytes in selector name list.\n", (unsigned long)(table_size * sizeof(void*)));
fprintf(stderr, "%d bytes in selector names.\n", selector_name_copies); fprintf(stderr, "%d bytes in selector names.\n", selector_name_copies);
fprintf(stderr, "%d bytes (%d entries) in selector hash table.\n", (int)(sel_table->table_size * fprintf(stderr, "%d bytes (%d entries) in selector hash table.\n", (int)(sel_table->table_size *
sizeof(struct selector_table_cell_struct)), sel_table->table_size); sizeof(struct selector_table_cell_struct)), sel_table->table_size);
@ -257,7 +275,8 @@ void objc_resize_dtables(uint32_t);
*/ */
PRIVATE void init_selector_tables() PRIVATE void init_selector_tables()
{ {
selector_list = SparseArrayNew(); selector_list = calloc(sizeof(void*), 4096);
table_size = 4096;
INIT_LOCK(selector_table_lock); INIT_LOCK(selector_table_lock);
selector_initialize(&sel_table, 4096); selector_initialize(&sel_table, 4096);
} }
@ -276,7 +295,19 @@ static inline void add_selector_to_table(SEL aSel, int32_t uid, uint32_t idx)
typeList->value = aSel->name; typeList->value = aSel->name;
typeList->next = 0; typeList->next = 0;
// Store the name. // Store the name.
SparseArrayInsert(selector_list, idx, typeList); if (idx >= table_size)
{
table_size *= 2;
struct sel_type_list **newList = calloc(sizeof(struct sel_type_list*), table_size);
if (newList == NULL)
{
abort();
}
memcpy(newList, selector_list, sizeof(void*)*(table_size/2));
free(selector_list);
selector_list = newList;
}
selector_list[idx] = typeList;
// Store the selector. // Store the selector.
selector_insert(sel_table, aSel); selector_insert(sel_table, aSel);
// Set the selector's name to the uid. // Set the selector's name to the uid.
@ -321,8 +352,7 @@ static inline void register_selector_locked(SEL aSel)
// Add this set of types to the list. // Add this set of types to the list.
// This is quite horrible. Most selectors will only have one type // This is quite horrible. Most selectors will only have one type
// encoding, so we're wasting a lot of memory like this. // encoding, so we're wasting a lot of memory like this.
struct sel_type_list *typeListHead = struct sel_type_list *typeListHead = selLookup_locked(untyped->index);
SparseArrayLookup(selector_list, untyped->index);
struct sel_type_list *typeList = struct sel_type_list *typeList =
(struct sel_type_list *)selector_pool_alloc(); (struct sel_type_list *)selector_pool_alloc();
typeList->value = aSel->types; typeList->value = aSel->types;
@ -400,8 +430,7 @@ static SEL objc_register_selector_copy(SEL aSel, BOOL copyArgs)
PRIVATE uint32_t sel_nextTypeIndex(uint32_t untypedIdx, uint32_t idx) PRIVATE uint32_t sel_nextTypeIndex(uint32_t untypedIdx, uint32_t idx)
{ {
struct sel_type_list *list = struct sel_type_list *list = selLookup(untypedIdx);
SparseArrayLookup(selector_list, untypedIdx);
if (NULL == list) { return 0; } if (NULL == list) { return 0; }
@ -431,8 +460,7 @@ const char *sel_getName(SEL sel)
const char *name = sel->name; const char *name = sel->name;
if (isSelRegistered(sel)) if (isSelRegistered(sel))
{ {
struct sel_type_list * list = struct sel_type_list * list = selLookup(sel->index);
SparseArrayLookup(selector_list, sel->index);
name = (list == NULL) ? NULL : list->value; name = (list == NULL) ? NULL : list->value;
} }
else else
@ -498,8 +526,7 @@ unsigned sel_copyTypes_np(const char *selName, const char **types, unsigned coun
SEL untyped = selector_lookup(selName, 0); SEL untyped = selector_lookup(selName, 0);
if (untyped == NULL) { return 0; } if (untyped == NULL) { return 0; }
struct sel_type_list *l = struct sel_type_list *l = selLookup(untyped->index);
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)untyped->name);
// Skip the head, which just contains the name, not the types. // Skip the head, which just contains the name, not the types.
l = l->next; l = l->next;
@ -532,8 +559,7 @@ unsigned sel_copyTypedSelectors_np(const char *selName, SEL *const sels, unsigne
SEL untyped = selector_lookup(selName, 0); SEL untyped = selector_lookup(selName, 0);
if (untyped == NULL) { return 0; } if (untyped == NULL) { return 0; }
struct sel_type_list *l = struct sel_type_list *l = selLookup(untyped->index);
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)untyped->name);
// Skip the head, which just contains the name, not the types. // Skip the head, which just contains the name, not the types.
l = l->next; l = l->next;
@ -599,8 +625,7 @@ SEL sel_get_typed_uid (const char *name, const char *types)
SEL sel = selector_lookup(name, types); SEL sel = selector_lookup(name, types);
if (NULL == sel) { return sel_registerTypedName_np(name, types); } if (NULL == sel) { return sel_registerTypedName_np(name, types); }
struct sel_type_list *l = struct sel_type_list *l = selLookup(sel->index);
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)sel->name);
// Skip the head, which just contains the name, not the types. // Skip the head, which just contains the name, not the types.
l = l->next; l = l->next;
if (NULL != l) if (NULL != l)
@ -616,8 +641,7 @@ SEL sel_get_any_typed_uid (const char *name)
SEL sel = selector_lookup(name, 0); SEL sel = selector_lookup(name, 0);
if (NULL == sel) { return sel_registerName(name); } if (NULL == sel) { return sel_registerName(name); }
struct sel_type_list *l = struct sel_type_list *l = selLookup(sel->index);
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)sel->name);
// Skip the head, which just contains the name, not the types. // Skip the head, which just contains the name, not the types.
l = l->next; l = l->next;
if (NULL != l) if (NULL != l)

Loading…
Cancel
Save