diff --git a/mak/COPY b/mak/COPY index 876b2cb71f..75dc52e0e8 100644 --- a/mak/COPY +++ b/mak/COPY @@ -22,6 +22,7 @@ COPY=\ $(IMPDIR)\core\internal\string.d \ $(IMPDIR)\core\internal\traits.d \ \ + $(IMPDIR)\core\stdc\clang_block.d \ $(IMPDIR)\core\stdc\complex.d \ $(IMPDIR)\core\stdc\config.d \ $(IMPDIR)\core\stdc\ctype.d \ diff --git a/mak/SRCS b/mak/SRCS index d1a9ba9725..c43cf55d32 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -23,6 +23,7 @@ SRCS=\ src\core\internal\string.d \ src\core\internal\traits.d \ \ + src\core\stdc\clang_block.d \ src\core\stdc\config.d \ src\core\stdc\ctype.d \ src\core\stdc\errno.d \ diff --git a/posix.mak b/posix.mak index e5d645bcd6..8ca675eb78 100644 --- a/posix.mak +++ b/posix.mak @@ -30,9 +30,11 @@ IMPDIR=import OPTIONAL_PIC:=$(if $(PIC),-fPIC,) ifeq (osx,$(OS)) + CLANG_BLOCKS:=1 DOTDLL:=.dylib DOTLIB:=.a else + CLANG_BLOCKS:=0 DOTDLL:=.so DOTLIB:=.a endif @@ -90,12 +92,6 @@ SRCS:=$(subst \,/,$(SRCS)) OBJS= $(ROOT)/errno_c.o $(ROOT)/bss_section.o $(ROOT)/threadasm.o -ifeq ($(OS),osx) -ifeq ($(MODEL), 64) - OBJS+=$(ROOT)/osx_tls.o -endif -endif - # build with shared library support SHARED=$(if $(findstring $(OS),linux freebsd),1,) @@ -194,6 +190,10 @@ HAS_ADDITIONAL_TESTS:=$(shell test -d test && echo 1) ifeq ($(HAS_ADDITIONAL_TESTS),1) ADDITIONAL_TESTS:=test/init_fini test/exceptions test/coverage test/profile ADDITIONAL_TESTS+=$(if $(SHARED),test/shared,) + + ifeq ($(CLANG_BLOCKS),1) + ADDITIONAL_TESTS+=test/clang_block + endif endif .PHONY : unittest diff --git a/src/core/stdc/clang_block.d b/src/core/stdc/clang_block.d new file mode 100644 index 0000000000..54cd84d5a6 --- /dev/null +++ b/src/core/stdc/clang_block.d @@ -0,0 +1,206 @@ +/** + * This module contains functionality for interfacing with + * $(LINK2 http://clang.llvm.org/docs/BlockLanguageSpec.html, Clang Blocks). + * + * The examples below first shows a C function, using Clang Blocks, to call and + * then the D code for calling the C function. + * + * Examples: + * + * Basic example of calling a function taking a block with no arguments that + * returns void. + * + * $(CCODE void foo(void (^block)(void));) + * + * Will look like: + * + * --- + * import core.stdc.clang_block; + * + * // The `Block` type is used to represent the Clang block in D + * extern(C) void foo(Block!()* block); + * + * void main() + * { + * // The `block` function is used to initialize an instance of `Block`. + * // A delegate will be passed to the `block` function which will be the + * // body of the block. + * auto b = block({ writeln("foo"); }); + * foo(&b); + * } + * --- + * + * Example of calling a function taking a block with arguments that returns + * void. + * + * $(CCODE void foo(void (^block)(int));) + * + * Will look like: + * + * --- + * import core.stdc.clang_block; + * + * // The type parameters to the instantiation of `Block` is first the return + * // type, `void`, and then the parameter types, `int`. + * extern(C) void foo(Block!(void, int)* block); + * + * void main() + * { + * // The delegate to the `block` function cannot use inferred type + * // parameters. + * auto b = block((int a){ writeln(a); }); + * foo(&b); + * } + * --- + * + * Example of calling a function taking a block with no arguments that returns + * an int. + * + * $(CCODE void foo(int (^block)(void));) + * + * Will look like: + * + * --- + * import core.stdc.clang_block; + * + * extern(C) void foo(Block!(int)* block); + * + * void main() + * { + * // The return type can be inferred + * auto b = block({ return 3; }); + * foo(&b); + * } + * --- + * + * To build on non Apple platforms the C code needs to be compiled using Clang + * and it's required to link with the + * $(LINK2 http://compiler-rt.llvm.org, Blocks runtime). On Debian based + * platforms install the following packages: + * "llvm", "clang" and "libblocksruntime-dev". Example of compiling on Linux: + * + * $(CCODE + * $ clang foo.c -o foo.o + * $ dmd main.d foo.o -L-lBlocksRuntime + * ) + * + * Copyright: Copyright Jacob Carlborg 2016. + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Authors: Jacob Carlborg + * Source: $(DRUNTIMESRC core/_clang_block.d) + */ +module core.stdc.clang_block; + +import core.stdc.config; + +/** + * This struct is the D representation of a Clang Block. + * + * Params: + * R = the return type of the block + * Params = the parameter types of the block + */ +struct Block(R = void, Params...) +{ +private: + + alias extern(C) R function(Block*, Params) Invoke; + + void* isa; + int flags; + int reserved = 0; + Invoke invoke; + Descriptor* descriptor; + + // Imported variables go here + R delegate(Params) dg; + + this(void* isa, int flags, Invoke invoke, R delegate(Params) dg) + { + this.isa = isa; + this.flags = flags; + this.invoke = invoke; + this.dg = dg; + this.descriptor = &.descriptor; + } +} + +/** + * Creates a new block that can be passed to a C function expecting a + * Clang Block. + * + * Params: + * R = the return type of the block + * Params = the parameter types of the block + * dg = the body of the block + * + * Returns: the newly created block + */ +Block!(R, Params) block(R, Params...)(R delegate(Params) dg) +{ + static if (Params.length == 0) + enum flags = 0x50000000; + else + enum flags = 0x40000000; + + return Block!(R, Params)( + &_NSConcreteStackBlock, flags, &invoke!(R, Params), dg + ); +} + +private: + +/* + * The the block implementation specification is available here: + * http://clang.llvm.org/docs/Block-ABI-Apple.html + */ + +// Block descriptor +struct Descriptor +{ + // null/0 + c_ulong reserved; + + // Block!(R, Params).sizeof + c_ulong size; + + // Optional helper functions + // extern(C) void function(void* dst, void* src) copy_helper; + // extern(C) void function(void* src) dispose_helper; + + /* + * Signature of the block, using Objective-C type encoding. + * Seems not to be used. + */ + const(char)* signature; +} + +// Block of uninitialized memory used for stack block literals +extern(C) extern __gshared void*[32] _NSConcreteStackBlock; + +/* + * Shared block descriptor. Since the descriptor will always look the same we + * can reuse a single descriptor. + */ +__gshared auto descriptor = Descriptor(0, Block!().sizeof); + +/* + * The body of a block that the C runtime will call. + * + * The implementation forwards to the delegate stored in the block struct + * containing the real D body. + * + * Since a delegate is used to store the actual D body of the block a single + * body can be shared for all blocks. + * + * Params: + * R = the return type of the block + * Args = the parameter types of the block + * args = the argument that was passed to the block + * + * Returns: whatever the delegate stored in the block returns + */ +extern(C) R invoke(R, Args...)(Block!(R, Args)* block, Args args) +{ + return block.dg(args); +} diff --git a/src/rt/osx_tls.c b/src/rt/osx_tls.c deleted file mode 100644 index 6cdffff672..0000000000 --- a/src/rt/osx_tls.c +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Helpers for determining TLS memory ranges on OS X. - * - * This unfortunately cannot be entirely done in D, as the OS X API uses - * the Apple-specific blocks C extension. - * - * Copyright: David Nadlinger, 2012. - * License: Boost License 1.0. - * Authors: David Nadlinger - */ - -#ifndef __APPLE__ -#ifndef __BLOCKS__ - #error "Need a C compiler with Apple Blocks support – not building on OS X with Clang?" -#endif -#endif - -#include -#include -#include - -/* - * Declarations from dyld_priv.h, available on 10.7+. - */ -enum dyld_tlv_states { - dyld_tlv_state_allocated = 10, - dyld_tlv_state_deallocated = 20 -}; -typedef struct { - size_t info_size; - void * tlv_addr; - size_t tlv_size; -} dyld_tlv_info; -typedef void (^dyld_tlv_state_change_handler)(enum dyld_tlv_states state, const dyld_tlv_info *info); -extern void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler); -extern void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler); - -void _d_dyld_getTLSRange(void* arbitraryTLSSymbol, void** start, size_t* size) { - dyld_enumerate_tlv_storage( - ^(enum dyld_tlv_states state, const dyld_tlv_info *info) { - assert(state == dyld_tlv_state_allocated); - if (info->tlv_addr <= arbitraryTLSSymbol && - arbitraryTLSSymbol < (info->tlv_addr + info->tlv_size) - ) { - // Found the range. - *start = info->tlv_addr; - *size = info->tlv_size; - } - } - ); -} diff --git a/src/rt/sections_osx_x86_64.d b/src/rt/sections_osx_x86_64.d index 1bfcef3680..5fcd40c21d 100644 --- a/src/rt/sections_osx_x86_64.d +++ b/src/rt/sections_osx_x86_64.d @@ -24,6 +24,7 @@ version(Darwin): version(X86_64): // debug = PRINTF; +import core.stdc.clang_block; import core.stdc.stdio; import core.stdc.string, core.stdc.stdlib; import core.sys.posix.pthread; @@ -95,11 +96,9 @@ void finiSections() void[] initTLSRanges() { - void* start = null; - size_t size = 0; - _d_dyld_getTLSRange(&dummyTlsSymbol, &start, &size); - assert(start && size, "Could not determine TLS range."); - return start[0 .. size]; + auto range = getTLSRange(); + assert(range.isValid, "Could not determine TLS range."); + return range.toArray(); } void finiTLSRanges(void[] rng) @@ -114,11 +113,65 @@ void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothr private: -extern(C) void _d_dyld_getTLSRange(void*, void**, size_t*); +// Declarations from dyld_priv.h in dyld, available on 10.7+. +enum dyld_tlv_states +{ + allocated = 10, + deallocated = 20 +} + +struct dyld_tlv_info +{ + size_t info_size; + void * tlv_addr; + size_t tlv_size; +} + +alias dyld_tlv_state_change_handler = Block!(void, dyld_tlv_states, dyld_tlv_info*)*; +extern(C) void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler); -__gshared SectionGroup _sections; ubyte dummyTlsSymbol; +struct TLSRange +{ + void* start; + size_t size; + + bool isValid() + { + return start !is null && size > 0; + } + + void[] toArray() + { + return start[0 .. size]; + } +} + +TLSRange getTLSRange() +{ + void* tlsSymbol = &dummyTlsSymbol; + TLSRange range; + + scope dg = (dyld_tlv_states state, dyld_tlv_info* info) { + assert(state == dyld_tlv_states.allocated); + + if (info.tlv_addr <= tlsSymbol && tlsSymbol < + (info.tlv_addr + info.tlv_size) + ) + { + range = TLSRange(info.tlv_addr, info.tlv_size); + } + }; + + auto handler = block(dg); + dyld_enumerate_tlv_storage(&handler); + + return range; +} + +__gshared SectionGroup _sections; + extern (C) void sections_osx_onAddImage(in mach_header* h, intptr_t slide) { foreach (e; dataSegs) diff --git a/test/clang_block/Makefile b/test/clang_block/Makefile new file mode 100644 index 0000000000..8846dad56b --- /dev/null +++ b/test/clang_block/Makefile @@ -0,0 +1,21 @@ +include ../common.mak + +TESTS:=clang_block + +.PHONY: all clean +all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) + +$(ROOT)/%.done: $(ROOT)/% + @echo Testing $* + $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) + @touch $@ + +$(ROOT)/%_c: $(SRC)/%.c + @mkdir -p $(@D) + $(QUIET)$(CC) $(CFLAGS) -o $@.o -c $< + +$(ROOT)/%: $(SRC)/%.d $(ROOT)/%_c + $(QUIET)$(DMD) $(DFLAGS) -main -unittest -of$@ $@_c.o $< + +clean: + rm -rf $(GENERATED) diff --git a/test/clang_block/src/clang_block.c b/test/clang_block/src/clang_block.c new file mode 100644 index 0000000000..7775260b6b --- /dev/null +++ b/test/clang_block/src/clang_block.c @@ -0,0 +1,23 @@ +#ifndef __BLOCKS__ + #error "Need a C compiler with Blocks support – not building with Clang?" +#endif + +void voidVoidArgs(void(^block)(void)) +{ + block(); +} + +int intVoidArgs(int(^block)(void)) +{ + return block(); +} + +void voidIntArgs(void(^block)(int), int arg) +{ + block(arg); +} + +int intIntArgs(int(^block)(int), int arg) +{ + return block(arg); +} diff --git a/test/clang_block/src/clang_block.d b/test/clang_block/src/clang_block.d new file mode 100644 index 0000000000..54cbdffaf1 --- /dev/null +++ b/test/clang_block/src/clang_block.d @@ -0,0 +1,52 @@ +import core.stdc.clang_block; + +extern(C) void voidVoidArgs(Block!()*); +extern(C) int intVoidArgs(Block!(int)*); +extern(C) void voidIntArgs(Block!(void, int)*, int); +extern(C) int intIntArgs(Block!(int, int)*, int); + +// block returning void taking no arguments +unittest +{ + enum value = 3; + + int result; + auto b = block({ result = value; }); + voidVoidArgs(&b); + assert(result == value); +} + +// block returning int taking no arguments +unittest +{ + enum value = 3; + + auto b = block({ return value; }); + auto result = intVoidArgs(&b); + assert(result == value); +} + +// block returning void taking int argument +unittest +{ + enum value = 3; + + int result; + auto b = block((int v) { + result = v; + }); + voidIntArgs(&b, value); + assert(result == value); +} + +// block returning int taking int argument +unittest +{ + enum value = 3; + + auto b = block((int v) { + return v; + }); + auto result = intIntArgs(&b, value); + assert(result == value); +} diff --git a/win32.mak b/win32.mak index a678e71365..bec788ee02 100644 --- a/win32.mak +++ b/win32.mak @@ -85,6 +85,9 @@ $(DOCDIR)\core_time.html : src\core\time.d $(DOCDIR)\core_vararg.html : src\core\vararg.d $(DMD) $(DDOCFLAGS) -Df$@ $(DOCFMT) $** +$(DOCDIR)\core_stdc_clang_block.html : src\core\stdc\clang_block.d + $(DMD) $(DDOCFLAGS) -Df$@ $(DOCFMT) $** + $(DOCDIR)\core_stdc_complex.html : src\core\stdc\complex.d $(DMD) $(DDOCFLAGS) -Df$@ $(DOCFMT) $** @@ -278,6 +281,9 @@ $(IMPDIR)\core\internal\string.d : src\core\internal\string.d $(IMPDIR)\core\internal\traits.d : src\core\internal\traits.d copy $** $@ +$(IMPDIR)\core\stdc\clang_block.d : src\core\stdc\clang_block.d + copy $** $@ + $(IMPDIR)\core\stdc\complex.d : src\core\stdc\complex.d copy $** $@ diff --git a/win64.mak b/win64.mak index cc61982605..23fc1c456e 100644 --- a/win64.mak +++ b/win64.mak @@ -93,6 +93,9 @@ $(DOCDIR)\core_vararg.html : src\core\vararg.d $(DMD) $(DDOCFLAGS) -Df$@ $(DOCFMT) $** +$(DOCDIR)\core_stdc_clang_block.html : src\core\stdc\clang_block.d + $(DMD) $(DDOCFLAGS) -Df$@ $(DOCFMT) $** + $(DOCDIR)\core_stdc_complex.html : src\core\stdc\complex.d $(DMD) $(DDOCFLAGS) -Df$@ $(DOCFMT) $** @@ -286,6 +289,9 @@ $(IMPDIR)\core\internal\string.d : src\core\internal\string.d $(IMPDIR)\core\internal\traits.d : src\core\internal\traits.d copy $** $@ +$(IMPDIR)\core\stdc\clang_block.d : src\core\stdc\clang_block.d + copy $** $@ + $(IMPDIR)\core\stdc\complex.d : src\core\stdc\complex.d copy $** $@