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.
@ -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.
Highlights of this release include:
- Added API for tracing, allowing interposition on all message sends matching a
given selector.
- Improved the dispatch table representation to improve performance and cache
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
Alternatively, a tarball is available from:
https://github.com/gnustep/libobjc2/archive/v1.8.zip
https://github.com/gnustep/libobjc2/archive/v1.8.tar.gz
https://github.com/gnustep/libobjc2/archive/v1.x.zip
https://github.com/gnustep/libobjc2/archive/v1.x.tar.gz
The runtime library is responsible for implementing the core features of the
object model, as well as exposing introspection features to the user. The

@ -126,7 +126,7 @@ else ()
add_definitions(-DNO_LEGACY)
endif ()
find_package(LLVM QUIET)
#find_package(LLVM QUIET)
set(DEFAULT_ENABLE_LLVM ${LLVM_FOUND})
if (DEFAULT_ENABLE_LLVM)
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; }
@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

@ -21,6 +21,7 @@ __attribute__((objc_root_class))
+(struct big)sret;
@end
int main(void)
{
__block int b = 0;
@ -32,12 +33,16 @@ int main(void)
char *type = block_copyIMPTypeEncoding_np(blk);
assert(NULL != 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);
assert(2 == [Foo count: 2]);
assert(4 == [Foo count: 2]);
assert(6 == [Foo count: 2]);
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) {
struct big b = {1, 2, 3, 4, 5};

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

@ -7,7 +7,7 @@
#pragma GCC diagnostic ignored "-Wobjc-property-no-attribute"
enum FooManChu { FOO, MAN, CHU };
struct YorkshireTeaStruct { int pot; char lady; };
struct YorkshireTeaStruct { int pot; signed char lady; };
typedef struct YorkshireTeaStruct YorkshireTeaStructType;
union MoneyUnion { float alone; double down; };
@ -22,7 +22,7 @@ __attribute__((objc_root_class))
{
@public
Class isa;
char charDefault;
signed char charDefault;
double doubleDefault;
enum FooManChu enumDefault;
float floatDefault;
@ -43,18 +43,18 @@ __attribute__((objc_root_class))
int intReadonlyGetter;
int intReadwrite;
int intAssign;
id idDefault;
__unsafe_unretained id idDefault;
id idRetain;
id idCopy;
id idWeak;
__weak id idWeak;
id idStrong;
int intNonatomic;
id idReadonlyCopyNonatomic;
id idReadonlyRetainNonatomic;
id idReadonlyWeakNonatomic;
__weak id idReadonlyWeakNonatomic;
id _idOther;
}
@property char charDefault;
@property signed char charDefault;
@property double doubleDefault;
@property enum FooManChu enumDefault;
@property float floatDefault;
@ -74,7 +74,7 @@ __attribute__((objc_root_class))
@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter;
@property(readwrite) int intReadwrite;
@property(assign) int intAssign;
@property id idDefault;
@property(unsafe_unretained) id idDefault;
@property(retain) id idRetain;
@property(copy) id idCopy;
@property(weak) id idWeak;
@ -129,10 +129,11 @@ __attribute__((objc_root_class))
@synthesize idOther = _idOther;
@dynamic idDynamic;
@dynamic idDynamicGetterSetter;
- (void)_ARCCompliantRetainRelease {}
@end
@protocol ProtocolTest
@property char charDefault;
@property signed char charDefault;
@property double doubleDefault;
@property enum FooManChu enumDefault;
@property float floatDefault;
@ -152,7 +153,7 @@ __attribute__((objc_root_class))
@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter;
@property(readwrite) int intReadwrite;
@property(assign) int intAssign;
@property id idDefault;
@property(unsafe_unretained) id idDefault;
@property(retain) id idRetain;
@property(copy) id idCopy;
@property(weak) id idWeak;
@ -172,7 +173,7 @@ __attribute__((objc_root_class))
@interface PropertyProtocolTest <ProtocolTest>
{
Class isa;
char charDefault;
signed char charDefault;
double doubleDefault;
enum FooManChu enumDefault;
float floatDefault;
@ -193,15 +194,15 @@ __attribute__((objc_root_class))
int intReadonlyGetter;
int intReadwrite;
int intAssign;
id idDefault;
__unsafe_unretained id idDefault;
id idRetain;
id idCopy;
id idWeak;
__weak id idWeak;
id idStrong;
int intNonatomic;
id idReadonlyCopyNonatomic;
id idReadonlyRetainNonatomic;
id idReadonlyWeakNonatomic;
__weak id idReadonlyWeakNonatomic;
id _idOther;
}
@end
@ -239,6 +240,7 @@ __attribute__((objc_root_class))
@synthesize idOther = _idOther;
@dynamic idDynamic;
@dynamic idDynamicGetterSetter;
- (void)_ARCCompliantRetainRelease {}
@end
#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) {
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) {
Ivar ivar = class_getInstanceVariable(objc_getClass("PropertyTest"), "structDefault");
return (struct YorkshireTeaStruct*)object_getIvar(self, ivar);
static struct YorkshireTeaStruct structDefault2Getter(id self, SEL _cmd) {
struct YorkshireTeaStruct *s;
object_getInstanceVariable(self, "structDefault", (void**)&s);
return *s;
}
void structDefault2Setter(id self, SEL _cmd, struct YorkshireTeaStruct* value) {
Ivar ivar = class_getInstanceVariable(objc_getClass("PropertyTest"), "structDefault");
object_setIvar(self, ivar, (id)value);
void structDefault2Setter(id self, SEL _cmd, struct YorkshireTeaStruct value) {
object_setInstanceVariable(self, "structDefault", &value);
}
int main(void)
@ -666,6 +668,7 @@ int main(void)
ATTR("V", "structDefault")));
PropertyTest* t = class_createInstance(testClass, 0);
assert(t != nil);
object_setClass(t, testClass);
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 <stdarg.h>
#include "../objc/runtime.h"
#include "../objc/hooks.h"
//#define assert(x) if (!(x)) { printf("Failed %d\n", __LINE__); }
@ -24,6 +25,30 @@ __attribute__((objc_root_class))
#endif
#endif
@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
- foo
{
@ -70,8 +95,87 @@ __attribute__((objc_root_class))
}
+ nothing { return 0; }
@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)
{
__objc_msg_forward2 = forward;
__objc_msg_forward3 = forward_slot;
TestCls = objc_getClass("Test");
int exceptionThrown = 0;
@try {
@ -85,6 +189,7 @@ int main(void)
assert((id)0x42 == objc_msgSend(TestCls, @selector(foo)));
objc_msgSend(TestCls, @selector(nothing));
objc_msgSend(TestCls, @selector(missing));
assert(forwardcalls == 1);
assert(0 == objc_msgSend(0, @selector(nothing)));
id a = objc_msgSend(objc_getClass("Test"), @selector(foo));
assert((id)0x42 == a);
@ -114,33 +219,38 @@ int main(void)
assert(0 == [f dzero]);
assert(0 == [f ldzero]);
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
const int iterations = 1000000000;
double times[3];
clock_t c1, c2;
c1 = clock();
for (int i=0 ; i<100000000 ; i++)
for (int i=0 ; i<iterations ; i++)
{
[TestCls nothing];
}
c2 = clock();
printf("Traditional message send took %f seconds. \n",
((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC);
times[0] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
fprintf(stderr, "Traditional message send took %f seconds. \n", times[0]);
c1 = clock();
for (int i=0 ; i<100000000 ; i++)
for (int i=0 ; i<iterations ; i++)
{
objc_msgSend(TestCls, @selector(nothing));
}
c2 = clock();
printf("objc_msgSend() message send took %f seconds. \n",
((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC);
times[1] = ((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));
c1 = clock();
for (int i=0 ; i<100000000 ; i++)
for (int i=0 ; i<iterations ; i++)
{
nothing(TestCls, @selector(nothing));
}
c2 = clock();
printf("Direct IMP call took %f seconds. \n",
((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC);
times[2] = ((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
return 0;
}

29
arc.m

@ -525,10 +525,27 @@ void* block_load_weak(void *block);
id objc_storeWeak(id *addr, id obj)
{
id old = *addr;
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))
{
intptr_t *refCount = ((intptr_t*)obj) - 1;
if (obj && *refCount < 0)
{
obj = nil;
cls = Nil;
}
}
LOCK_FOR_SCOPE(&weakRefLock);
if (nil != old)
@ -553,13 +570,10 @@ id objc_storeWeak(id *addr, id obj)
*addr = obj;
return nil;
}
Class cls = classForObject(obj);
if (&_NSConcreteGlobalBlock == cls)
if (isGlobalObject)
{
// 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
// TODO: We probably also want to do the same for constant strings and
// classes.
*addr = obj;
return obj;
}
@ -650,6 +664,11 @@ id objc_loadWeakRetained(id* addr)
LOCK_FOR_SCOPE(&weakRefLock);
id obj = *addr;
if (nil == obj) { return nil; }
// Small objects don't need reference count modification
if (isSmallObject(obj))
{
return obj;
}
Class cls = classForObject(obj);
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
static void *executeBuffer;
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");
if (NULL == tmp)
{
tmp = "/tmp/";
}
if (0 > asprintf(&tmpPattern, "%s/objc_trampolinesXXXXXXXXXXX", tmp))
struct block_header
{
abort();
}
}
static int getAnonMemFd(void)
void *block;
void(*fnptr)(void);
/**
* On 64-bit platforms, we have 16 bytes for instructions, which ought to
* be enough without padding. On MIPS, we need
* 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
* `init_trampolines` will fire on library load.
*/
#if defined(__i386__) || (defined(__mips__) && !defined(__mips_n64))
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);
int fd = mkstemp(pattern);
unlink(pattern);
free(pattern);
return fd;
}
#else
static void initTmpFile(void) {}
static int getAnonMemFd(void)
struct block_header headers[HEADERS_PER_PAGE];
char rx_buffer[PAGE_SIZE];
};
_Static_assert(__builtin_offsetof(struct trampoline_buffers, rx_buffer) == PAGE_SIZE,
"Incorrect offset for read-execute buffer");
_Static_assert(sizeof(struct trampoline_buffers) == 2*PAGE_SIZE,
"Incorrect size for trampoline buffers");
struct trampoline_set
{
return shm_open(SHM_ANON, O_CREAT | O_RDWR, 0);
}
#endif
struct trampoline_buffers *buffers;
struct trampoline_set *next;
int first_free;
};
static mutex_t trampoline_lock;
struct wx_buffer
{
void *w;
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)
{
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);
initTmpFile();
}
static struct wx_buffer alloc_buffer(size_t size)
static id invalid(id self, SEL _cmd)
{
LOCK_FOR_SCOPE(&trampoline_lock);
if ((0 == offset) || (offset + size >= PAGE_SIZE))
fprintf(stderr, "Invalid block method called for [%s %s]\n",
class_getName(object_getClass(self)), sel_getName(_cmd));
return nil;
}
static struct trampoline_set *alloc_trampolines(char *start, char *end)
{
int fd = getAnonMemFd();
ftruncate(fd, PAGE_SIZE);
void *w = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
executeBuffer = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
*((void**)w) = writeBuffer;
writeBuffer = w;
offset = sizeof(void*);
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++)
{
metadata->buffers->headers[i].fnptr = (void(*)(void))invalid;
metadata->buffers->headers[i].block = &metadata->buffers->headers[i+1].block;
char *block = metadata->buffers->rx_buffer + (i * sizeof(struct block_header));
memcpy(block, start, end-start);
}
struct wx_buffer b = { writeBuffer + offset, executeBuffer + offset };
offset += size;
return b;
metadata->buffers->headers[HEADERS_PER_PAGE-1].block = NULL;
mprotect(metadata->buffers->rx_buffer, PAGE_SIZE, PROT_READ | PROT_EXEC);
clear_cache(metadata->buffers->rx_buffer, &metadata->buffers->rx_buffer[PAGE_SIZE]);
return metadata;
}
extern void __objc_block_trampoline;
extern void __objc_block_trampoline_end;
extern void __objc_block_trampoline_sret;
extern void __objc_block_trampoline_end_sret;
static struct trampoline_set *sret_trampolines;
static struct trampoline_set *trampolines;
IMP imp_implementationWithBlock(void *block)
{
struct Block_layout *b = block;
void *start;
void *end;
LOCK_FOR_SCOPE(&trampoline_lock);
struct trampoline_set **setptr;
if ((b->flags & BLOCK_USE_SRET) == BLOCK_USE_SRET)
{
setptr = &sret_trampolines;
start = &__objc_block_trampoline_sret;
end = &__objc_block_trampoline_end_sret;
}
else
{
setptr = &trampolines;
start = &__objc_block_trampoline;
end = &__objc_block_trampoline_end;
}
size_t trampolineSize = end - start;
// If we don't have a trampoline intrinsic for this architecture, return a
// null IMP.
if (0 >= trampolineSize) { return 0; }
struct wx_buffer buf = alloc_buffer(trampolineSize + 2*sizeof(void*));
void **out = buf.w;
out[0] = (void*)b->invoke;
out[1] = Block_copy(b);
memcpy(&out[2], start, trampolineSize);
out = buf.x;
char *newIMP = (char*)&out[2];
clear_cache(newIMP, newIMP+trampolineSize);
return (IMP)newIMP;
block = Block_copy(block);
// Allocate some trampolines if this is the first time that we need to do this.
if (*setptr == NULL)
{
*setptr = alloc_trampolines(start, end);
}
for (struct trampoline_set *set=*setptr ; set!=NULL ; set=set->next)
{
if (set->first_free != -1)
{
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);
void *e = executeBuffer;
void *w = writeBuffer;
UNLOCK(&trampoline_lock);
while (e)
for (struct trampoline_set *set=*setptr ; set!=NULL ; set=set->next)
{
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)
{
if (0 == isBlockIMP((void*)anImp)) { return 0; }
return *(((void**)anImp) - 1);
LOCK_FOR_SCOPE(&trampoline_lock);
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)
{
void *w = isBlockIMP((void*)anImp);
if (0 == w) { return NO; }
Block_release(((void**)anImp) - 1);
LOCK_FOR_SCOPE(&trampoline_lock);
struct trampoline_set *set = trampolines;
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;
}

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

@ -18,6 +18,24 @@ struct objc_bitfield
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
{
/**
@ -141,13 +159,13 @@ struct objc_class
* bits are set, from low to high, for each ivar in the object that is a
* strong pointer.
*/
intptr_t strong_pointers;
uintptr_t strong_pointers;
/**
* 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
* strong_pointers field.
*/
intptr_t weak_pointers;
uintptr_t weak_pointers;
};
/**

@ -2,6 +2,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include "objc/runtime.h"
#include "objc/hooks.h"
#include "sarray2.h"
@ -12,7 +13,16 @@
#include "slot_pool.h"
#include "dtable.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;
#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);
}
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(
struct objc_method_list *list,
SparseArray *sarray,
@ -644,23 +698,36 @@ PRIVATE void objc_resize_dtables(uint32_t newSize)
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)
tracing_dtable = SparseArrayExpandingArray(tracing_dtable, dtable_depth);
#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
void *e = NULL;
struct objc_class *next;
while ((next = class_table_next(&e)))
{
if (next->dtable != (void*)uninstalled_dtable &&
NULL != next->dtable &&
((SparseArray*)next->dtable)->mask == oldMask)
if (next->dtable == old_uninstalled_dtable)
{
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);
SparseArrayExpandingArray((void*)next->isa->dtable, dtable_depth);
next->dtable = SparseArrayExpandingArray((void*)next->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.
*/
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);
}
#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++));
// 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,
exceptionObject, context, YES);
}
// FIXME!
#ifndef __arm__
BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0)
if (exceptionClass == objc_exception_class)
{
@ -497,17 +496,15 @@ BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0)
id *newEx = __cxa_allocate_exception(sizeof(id));
*newEx = ex->object;
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_cleanup = cleanup;
ex->cxx_exception->private_1 = exceptionObject->private_1;
ex->cxx_exception->private_2 = exceptionObject->private_2;
}
exceptionObject = ex->cxx_exception;
exceptionClass = cxx_exception_class;
}
return CALL_PERSONALITY_FUNCTION(__gxx_personality_v0);
}
#endif
// 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

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

@ -288,7 +288,7 @@ static int PREFIX(_insert)(PREFIX(_table) *table,
return 1;
}
/* 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_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.
int hop = __builtin_ffs(jump);
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->secondMaps &= ~(1 << (hop-1));
if (0 == hopCell->secondMaps)

@ -1,6 +1,8 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "objc/runtime.h"
#include "objc/objc-arc.h"
#include "class.h"
#include "ivar.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
////////////////////////////////////////////////////////////////////////////////
void object_setIvar(id object, Ivar ivar, id value)
{
char *addr = (char*)object;
addr += ivar_getOffset(ivar);
*(id*)addr = value;
ownershipForIvar(object_getClass(object), ivar);
id *addr = (id*)((char*)object + ivar_getOffset(ivar));
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 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;
}
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)
@ -170,7 +250,7 @@ Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)
Ivar ivar = class_getInstanceVariable(object_getClass(obj), name);
if (NULL != outValue)
{
*outValue = object_getIvar(obj, ivar);
*outValue = (((char*)obj) + ivar_getOffset(ivar));
}
return ivar;
}

@ -1,4 +1,5 @@
#include "common.S"
#include "asmconstants.h"
#if __x86_64
#include "objc_msgSend.x86-64.S"
#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
.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 LOG reg
@ -37,23 +55,23 @@
ldr r5, [\sel] // selector->index -> r5
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
beq 1f
teq r6, #0
beq 2f
and r6, r5, #0xff0000
ldr r4, [r4, r6, asr#14]
ldr r4, [r4, #DATA_OFFSET]
byte3 r6, r5 // Put byte 3 of the sel id in r6
add r6, r4, r6, lsl #2 // r6 = dtable address + dtable data offset
ldr r4, [r6, #DATA_OFFSET] // Load, adding in the data offset
1: // dtable16
and r6, r5, #0xff00
ldr r4, [r4, r6, asr#6]
ldr r4, [r4, #DATA_OFFSET]
byte2 r6, r5 // Put byte 2 of the sel id in r6
add r6, r4, r6, lsl #2 // r6 = dtable address + dtable data offset
ldr r4, [r6, #DATA_OFFSET] // Load, adding in the data offset
2: // dtable8
and r6, r5, #0xff
ldr ip, [r4, r6, asl#2]
byte1 r6, r5 // Low byte of sel id into r5
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
beq 5f // Go to the slow path and do the forwarding stuff
@ -71,10 +89,14 @@
5: // Slow lookup
push {r0-r4, lr} // Save anything that will be clobbered by the call
.save {r0-r4, lr}
#ifndef __SOFTFP__
vpush {q0-q3}
.pad #64
#endif
push {\receiver} // &self, _cmd in arguments
.save {\receiver}
mov r0, sp
mov r1, \sel
@ -82,6 +104,9 @@
mov ip, r0 // IMP -> ip
pop {r5} // restore (modified) self to r5
#ifndef __SOFTFP__
vpop {q0-q3}
#endif
pop {r0-r4, lr} // Load clobbered registers
mov \receiver, r5
b 3b

@ -3,20 +3,10 @@
#ifdef _ABI64
#define LP ld
#define SP sd
#define DTABLE_OFFSET 64
#define SMALLOBJ_MASK 7
#define SHIFT_OFFSET 4
#define DATA_OFFSET 16
#define SLOT_OFFSET 32
#else
#warning N32 is untested, O32 is unsupported.
#define LP lw
#define SP sw
#define DTABLE_OFFSET 32
#define SMALLOBJ_MASK 1
#define SHIFT_OFFSET 4
#define DATA_OFFSET 12
#define SLOT_OFFSET 16
#endif
.macro dump_and_crash reg
@ -51,62 +41,62 @@ lw $zero, ($zero)
daddiu $t8, $t8, %lo(%neg(%gp_rel(0b)))
andi $t4, \receiver, SMALLOBJ_MASK # Check if the receiver is a small object
bne $t4, $0, 6f # Get the small object class
andi $t0, \receiver, SMALLOBJ_MASK # Check if the receiver is a small object
bne $t0, $0, 6f # Get the small object class
nop
LP $t5, (\sel)
LP $t1, (\sel)
# By this point, we have a non-nil
# receiver that is a real pointer
LP $t4, (\receiver) # Load the class
LP $t0, (\receiver) # Load the class
1: # class loaded, stored in $t4
LP $t4, DTABLE_OFFSET($t4) # Load the dtable from the class
lw $t6, SHIFT_OFFSET($t4) # Load the shift (dtable size)
# $t4 = dtable, $t5 = sel index
LP $t7, DATA_OFFSET($t4) # Load the address of the start of the array
1: # class loaded, stored in $t0
LP $t0, DTABLE_OFFSET($t0) # Load the dtable from the class
lw $t2, SHIFT_OFFSET($t0) # Load the shift (dtable size)
# $t0 = dtable, $t1 = sel index
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
daddi $v0, $t6, -8
beq $0, $t2, 3f # If this is a small dtable, jump to the small dtable handlers
daddi $v0, $t2, -8
beq $0, $v0, 2f
lui $t6, 0x00ff # The mask for a big dtable won't fit in an and immediate
and $t6, $t6, $t5 # mask the selector
lui $t2, 0x00ff # The mask for a big dtable won't fit in an and immediate
and $t2, $t2, $t1 # mask the selector
#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
srl $t6, $t6, 14
srl $t2, $t2, 14
#endif
dadd $t6, $t6, $t7
LP $t7, ($t6)
LP $t7, DATA_OFFSET($t7)
dadd $t2, $t2, $t3
LP $t3, ($t2)
daddi $t3, $t3, DATA_OFFSET # Compute the address of the start of the array
2: # dtable16:
andi $t6, $t5, 0xff00 # mask the selector
andi $t2, $t1, 0xff00 # mask the selector
#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
srl $t6, $t6, 6
srl $t2, $t2, 6
#endif
dadd $t6, $t6, $t7
LP $t7, ($t6)
LP $t7, DATA_OFFSET($t7)
dadd $t2, $t2, $t3
LP $t3, ($t2)
daddi $t3, $t3, DATA_OFFSET # Compute the address of the start of the array
3: # dtable8:
andi $t6, $t5, 0xff # mask the selector
andi $t2, $t1, 0xff # mask the selector
#ifdef _ABI64
dsll $t6, $t6, 3 # Left shift by pointer size
dsll $t2, $t2, 3 # Left shift by pointer size
#else
sll $t6, $t6, 2
sll $t2, $t2, 2
#endif
dadd $t6, $t6, $t7
LP $t7, ($t6)
dadd $t2, $t2, $t3
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
LP $25, SLOT_OFFSET($t7)
LP $25, SLOT_OFFSET($t3)
jr $25
nop
4: # returnNil:
@ -163,7 +153,8 @@ lw $zero, ($zero)
daddiu $a1, $a2, 0 # replace self with _cmd in $a1
.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
nop
@ -192,14 +183,14 @@ lw $zero, ($zero)
daddiu $sp, $sp, SAVE_SIZE
6: # smallObject:
#if _ABI64
dsll $t4, $t4, 3 # Convert tag to pointer offset
LP $t6, %got_disp(CDECL(SmallObjectClasses))($t8) # Load small object classes array address
daddu $t4, $t4, $t6 # Add the base address to the offset
dsll $t0, $t0, 3 # Convert tag to pointer offset
LP $t2, %got_disp(CDECL(SmallObjectClasses))($t8) # Load small object classes array address
daddu $t0, $t0, $t2 # Add the base address to the offset
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
b 1b
LP $t4, %got_disp(CDECL(SmallIntClass))($t8)
LP $t0, %got_disp(CDECL(SmallIntClass))($t8)
#endif
.cfi_endproc
.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
.cfi_startproc
movl \receiver(%esp), %eax
@ -24,30 +19,20 @@
# %edx: selector index fragment
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
je 2f
cmpl $0, %edx
je 3f
mov %ecx, %edx
and $0xff0000, %edx
shrl $14, %edx # Right shift 16, but then left shift by 2 (* sizeof(void*))
add %edx, %eax
mov (%eax), %eax
mov DATA_OFFSET(%eax), %eax
shrl $16, %edx
movl DATA_OFFSET(%eax, %edx, 4), %eax
2: # dtable16:
mov %ecx, %edx
and $0xff00, %edx
shrl $6, %edx
add %edx, %eax
mov (%eax), %eax
mov DATA_OFFSET(%eax), %eax
movzbl %ch, %edx
movl DATA_OFFSET(%eax, %edx, 4), %eax
3: # dtable8:
and $0xff, %ecx
shll $2, %ecx
add %ecx, %eax
mov (%eax), %eax
movzbl %cl, %edx
movl DATA_OFFSET(%eax, %edx, 4), %eax
test %eax, %eax
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
.cfi_startproc # Start emitting unwind data. We
@ -19,40 +14,27 @@
mov (\receiver), %r10 # Load the dtable from the class
1: # classLoaded
mov DTABLE_OFFSET(%r10), %r10 # Load the dtable from the class
push %r12
push %r13
mov (\sel), %r11 # Load the selector index
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
mov DTABLE_OFFSET(%r10), %r10 # Load the dtable from the class into r10
mov %rax, -8(%rsp) # %rax contains information for variadic calls
mov %rbx, -16(%rsp) # On the fast path, spill into the red zone
mov (\sel), %eax # Load the selector index into %eax
mov SHIFT_OFFSET(%r10), %r11d # Load the shift (dtable size) into r11
cmpl $8, %r11d # If this is a small dtable, jump to the small dtable handlers
je 2f
cmpl $0, %r13d
cmpl $0, %r11d
je 3f
mov %r11, %r13
and $0xff0000, %r13
shrl $13, %r13d # Right shift 16, but then left shift by 3 *sizeof(void*)
add %r13, %r12
mov (%r12), %r12
mov DATA_OFFSET(%r12), %r12
movl %eax, %r11d
shrl $16, %r11d
movq DATA_OFFSET(%r10, %r11, 8), %r10
2: # dtable16:
mov %r11, %r13
and $0xff00, %r13
shrl $5, %r13d
add %r13, %r12
mov (%r12), %r12
mov DATA_OFFSET(%r12), %r12
movzbl %ah, %ebx
movq DATA_OFFSET(%r10, %rbx, 8), %r10
3: # dtable8:
mov %r11, %r13
and $0xff, %r13
shll $3, %r13d
add %r13, %r12
mov (%r12), %r10
pop %r13
pop %r12
movzbl %al, %ebx
mov -8(%rsp), %rax
movq DATA_OFFSET(%r10, %rbx, 8), %r10
mov -16(%rsp), %rbx
test %r10, %r10
jz 5f # Nil slot - invoke some kind of forwarding mechanism
mov SLOT_OFFSET(%r10), %r10
@ -259,10 +241,8 @@
jmp 7b
6: # smallObject:
and \receiver, %r10 # Find the small int type
shll $3, %r10d
lea CDECL(SmallObjectClasses)(%rip), %r11
add %r11, %r10
mov (%r10), %r10
mov (%r11, %r10, 8), %r10
jmp 1b
.cfi_endproc
.endm

@ -45,6 +45,17 @@ struct objc_protocol2 *protocol_for_name(const char *name)
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)
{
int isEmpty =
@ -497,7 +508,7 @@ Protocol*__unsafe_unretained* objc_copyProtocolList(unsigned int *outCount)
Protocol *objc_allocateProtocol(const char *name)
{
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);
return p;
}
@ -506,7 +517,7 @@ void objc_registerProtocol(Protocol *proto)
if (NULL == proto) { return; }
LOCK_RUNTIME_FOR_SCOPE();
if (objc_getProtocol(proto->name) != NULL) { return; }
if (nil != proto->isa) { return; }
if (incompleteProtocolClass() != proto->isa) { return; }
proto->isa = ObjC2ProtocolClass;
protocol_table_insert((struct objc_protocol2*)proto);
}
@ -517,7 +528,7 @@ void protocol_addMethodDescription(Protocol *aProtocol,
BOOL isInstanceMethod)
{
if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; }
if (nil != aProtocol->isa) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; }
Protocol2 *proto = (Protocol2*)aProtocol;
struct objc_method_description_list **listPtr;
if (isInstanceMethod)
@ -561,6 +572,7 @@ void protocol_addMethodDescription(Protocol *aProtocol,
void protocol_addProtocol(Protocol *aProtocol, Protocol *addition)
{
if ((NULL == aProtocol) || (NULL == addition)) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; }
Protocol2 *proto = (Protocol2*)aProtocol;
if (NULL == proto->protocol_list)
{
@ -584,7 +596,7 @@ void protocol_addProperty(Protocol *aProtocol,
BOOL isInstanceProperty)
{
if ((NULL == aProtocol) || (NULL == name)) { return; }
if (nil != aProtocol->isa) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; }
if (!isInstanceProperty) { return; }
Protocol2 *proto = (Protocol2*)aProtocol;
struct objc_property_list **listPtr;

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

@ -6,17 +6,12 @@
#include "sarray2.h"
#include "visibility.h"
static void *EmptyArrayData[256];
static SparseArray EmptyArray = { 0xff, 0, 0, (void**)&EmptyArrayData};
static void *EmptyArrayData8[256] = { [0 ... 255] = &EmptyArray };
static SparseArray EmptyArray8 = { 0xff00, 8, 0, (void**)&EmptyArrayData8};
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};
const static SparseArray EmptyArray = { 0, 0, .data[0 ... 255] = 0 };
const static SparseArray EmptyArray8 = { 8, 0, .data[0 ... 255] = (void*)&EmptyArray};
const static SparseArray EmptyArray16 = { 16, 0, .data[0 ... 255] = (void*)&EmptyArray8};
const static SparseArray EmptyArray24 = { 24, 0, .data[0 ... 255] = (void*)&EmptyArray16};
#define MAX_INDEX(sarray) (sarray->mask >> sarray->shift)
#define DATA_SIZE(sarray) ((sarray->mask >> sarray->shift) + 1)
#define MAX_INDEX(sarray) (0xff)
// Tweak this value to trade speed for memory usage. Bigger values use more
// memory, but give faster lookups.
@ -29,19 +24,18 @@ void *EmptyChildForShift(uint32_t shift)
{
default: UNREACHABLE("Broken sparse array");
case 8:
return &EmptyArray;
return (void*)&EmptyArray;
case 16:
return &EmptyArray8;
return (void*)&EmptyArray8;
case 24:
return &EmptyArray16;
return (void*)&EmptyArray16;
case 32:
return &EmptyArray24;
return (void*)&EmptyArray24;
}
}
static void init_pointers(SparseArray * sarray)
{
sarray->data = calloc(DATA_SIZE(sarray), sizeof(void*));
if(sarray->shift != 0)
{
void *data = EmptyChildForShift(sarray->shift);
@ -57,7 +51,6 @@ PRIVATE SparseArray * SparseArrayNewWithDepth(uint32_t depth)
SparseArray * sarray = calloc(1, sizeof(SparseArray));
sarray->refCount = 1;
sarray->shift = depth-base_shift;
sarray->mask = base_mask << sarray->shift;
init_pointers(sarray);
return sarray;
}
@ -77,24 +70,17 @@ PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new
assert(sarray->refCount == 1);
SparseArray *new = calloc(1, sizeof(SparseArray));
new->refCount = 1;
new->shift = sarray->shift;
new->mask = sarray->mask;
void **newData = malloc(DATA_SIZE(sarray) * sizeof(void*));
void *data = EmptyChildForShift(new->shift + 8);
new->shift = sarray->shift + 8;
new->data[0] = sarray;
void *data = EmptyChildForShift(new->shift);
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
// all non-zero values shifted away, resulting in 0. All lookups will
// therefore go to the new sarray.
sarray->shift += base_shift;
// Finally, set the mask to the correct value. Now all lookups should work.
sarray->mask <<= base_shift;
return sarray;
return new;
}
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
// nodes.
uint32_t zeromask = ~(sarray->mask >> base_shift);
uint32_t zeromask = ~((0xff << sarray->shift) >> base_shift);
while (j<max)
{
//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->mask = sarray->mask >> base_shift;
init_pointers(newsarray);
sarray->data[i] = newsarray;
child = newsarray;
@ -194,25 +179,28 @@ PRIVATE void SparseArrayInsert(SparseArray * sarray, uint32_t index, void *value
}
else
{
sarray->data[index & sarray->mask] = value;
sarray->data[MASK_INDEX(index)] = value;
}
}
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->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 (sarray->shift > 0)
{
for (unsigned int i = 0 ; i<=MAX_INDEX(sarray); i++)
{
SparseArray *child = copy->data[i];
if (!(child == &EmptyArray ||
child == &EmptyArray8 ||
child == &EmptyArray16 ||
child == &EmptyArray24))
{
__sync_fetch_and_add(&child->refCount, 1);
}
// Non-lazy copy. Uncomment if debugging
// copy->data[i] = SparseArrayCopy(copy->data[i]);
}
@ -226,6 +214,7 @@ PRIVATE void SparseArrayDestroy(SparseArray * sarray)
if (sarray == &EmptyArray ||
sarray == &EmptyArray8 ||
sarray == &EmptyArray16 ||
sarray == &EmptyArray24 ||
(__sync_sub_and_fetch(&sarray->refCount, 1) > 0))
{
return;
@ -233,13 +222,11 @@ PRIVATE void SparseArrayDestroy(SparseArray * sarray)
if(sarray->shift > 0)
{
uint32_t max = (sarray->mask >> sarray->shift) + 1;
for(uint32_t i=0 ; i<max ; i++)
for(uint32_t i=0 ; i<data_size ; i++)
{
SparseArrayDestroy((SparseArray*)sarray->data[i]);
}
}
free(sarray->data);
free(sarray);
}

@ -13,6 +13,17 @@
#include <stdlib.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
* quite RAM-intensive and could be optimised. Maps 32-bit integers to pointers.
@ -25,11 +36,6 @@
*/
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
* 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.
*/
void ** data;
void *data[data_size];
} SparseArray;
/**
* Turn an index in the array into an index in the current depth.
*/
#define MASK_INDEX(index) \
((index & sarray->mask) >> sarray->shift)
((index >> sarray->shift) & 0xff)
#define SARRAY_EMPTY ((void*)0)
/**
@ -64,7 +70,7 @@ typedef struct
static inline void* SparseArrayLookup(SparseArray * sarray, uint32_t index)
{
// 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 about half the memory usage.
uint32_t i = index;

@ -9,7 +9,6 @@
#include <assert.h>
#include <ctype.h>
#include "lock.h"
#include "sarray2.h"
#include "objc/runtime.h"
#include "method_list.h"
#include "class.h"
@ -37,10 +36,14 @@
* array.
*/
static uint32_t selector_count = 1;
/**
* Size of the buffer.
*/
static size_t table_size;
/**
* Mapping from selector numbers to selector names.
*/
PRIVATE SparseArray *selector_list = NULL;
PRIVATE struct sel_type_list **selector_list = NULL;
#ifdef DEBUG_SELECTOR_TABLE
#define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
@ -51,6 +54,27 @@ PRIVATE SparseArray *selector_list = NULL;
// Get the functions for string hashing
#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)
{
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;
if (isSelRegistered(sel))
{
struct sel_type_list * list =
SparseArrayLookup(selector_list, sel->index);
struct sel_type_list * list = selLookup_locked(sel->index);
name = (list == NULL) ? NULL : list->value;
}
if (NULL == name)
@ -226,16 +249,11 @@ static inline uint32_t hash_selector(const void *s)
*/
static selector_table *sel_table;
/**
* Lock protecting the selector table.
*/
mutex_t selector_table_lock;
static int selector_name_copies;
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 (%d entries) in selector hash table.\n", (int)(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()
{
selector_list = SparseArrayNew();
selector_list = calloc(sizeof(void*), 4096);
table_size = 4096;
INIT_LOCK(selector_table_lock);
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->next = 0;
// 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.
selector_insert(sel_table, aSel);
// 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.
// This is quite horrible. Most selectors will only have one type
// encoding, so we're wasting a lot of memory like this.
struct sel_type_list *typeListHead =
SparseArrayLookup(selector_list, untyped->index);
struct sel_type_list *typeListHead = selLookup_locked(untyped->index);
struct sel_type_list *typeList =
(struct sel_type_list *)selector_pool_alloc();
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)
{
struct sel_type_list *list =
SparseArrayLookup(selector_list, untypedIdx);
struct sel_type_list *list = selLookup(untypedIdx);
if (NULL == list) { return 0; }
@ -431,8 +460,7 @@ const char *sel_getName(SEL sel)
const char *name = sel->name;
if (isSelRegistered(sel))
{
struct sel_type_list * list =
SparseArrayLookup(selector_list, sel->index);
struct sel_type_list * list = selLookup(sel->index);
name = (list == NULL) ? NULL : list->value;
}
else
@ -498,8 +526,7 @@ unsigned sel_copyTypes_np(const char *selName, const char **types, unsigned coun
SEL untyped = selector_lookup(selName, 0);
if (untyped == NULL) { return 0; }
struct sel_type_list *l =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)untyped->name);
struct sel_type_list *l = selLookup(untyped->index);
// Skip the head, which just contains the name, not the types.
l = l->next;
@ -532,8 +559,7 @@ unsigned sel_copyTypedSelectors_np(const char *selName, SEL *const sels, unsigne
SEL untyped = selector_lookup(selName, 0);
if (untyped == NULL) { return 0; }
struct sel_type_list *l =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)untyped->name);
struct sel_type_list *l = selLookup(untyped->index);
// Skip the head, which just contains the name, not the types.
l = l->next;
@ -599,8 +625,7 @@ SEL sel_get_typed_uid (const char *name, const char *types)
SEL sel = selector_lookup(name, types);
if (NULL == sel) { return sel_registerTypedName_np(name, types); }
struct sel_type_list *l =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)sel->name);
struct sel_type_list *l = selLookup(sel->index);
// Skip the head, which just contains the name, not the types.
l = l->next;
if (NULL != l)
@ -616,8 +641,7 @@ SEL sel_get_any_typed_uid (const char *name)
SEL sel = selector_lookup(name, 0);
if (NULL == sel) { return sel_registerName(name); }
struct sel_type_list *l =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)sel->name);
struct sel_type_list *l = selLookup(sel->index);
// Skip the head, which just contains the name, not the types.
l = l->next;
if (NULL != l)

Loading…
Cancel
Save