From 9f1889eea6df61e3dd08fbb68f8b4c05ad4712a8 Mon Sep 17 00:00:00 2001 From: Jacob Carlborg Date: Wed, 25 May 2016 17:14:27 +0200 Subject: [PATCH 1/2] Add support for interfacing with Clang Blocks Clang Blocks, or just Blocks [1], is an extension to the C family of languages which adds support for anonymous functions, much like delegates in D. Being able to interface with Blocks is important when interfacing with Apple specific API's since some of the API's are only available in a form requiring using Blocks. Interfacing with Blocks is done by directly using the underlying ABI. A Block basically consist of a struct containing a function pointer to the body of the Block, any captured values and some metadata. The ABI that is used is the Apple ABI [2]. It's possible to use Blocks on non-Apple platforms as well. This requires to compile the C code using Clang and install and link with the Blocks runtime [3]. [1] Language specification for Blocks: http://clang.llvm.org/docs/BlockLanguageSpec.html [2] http://clang.llvm.org/docs/Block-ABI-Apple.html [3] http://compiler-rt.llvm.org --- mak/COPY | 1 + mak/SRCS | 1 + posix.mak | 6 + src/core/stdc/clang_block.d | 206 +++++++++++++++++++++++++++++ test/clang_block/Makefile | 21 +++ test/clang_block/src/clang_block.c | 23 ++++ test/clang_block/src/clang_block.d | 52 ++++++++ win32.mak | 6 + win64.mak | 6 + 9 files changed, 322 insertions(+) create mode 100644 src/core/stdc/clang_block.d create mode 100644 test/clang_block/Makefile create mode 100644 test/clang_block/src/clang_block.c create mode 100644 test/clang_block/src/clang_block.d 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..cad8bc2bda 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 @@ -194,6 +196,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/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 $** $@ From 550edd0a64f0eb2c4f35d3ec3d88e26b40ac779e Mon Sep 17 00:00:00 2001 From: Jacob Carlborg Date: Sun, 29 May 2016 16:57:05 +0200 Subject: [PATCH 2/2] Port osx_tls.c to D --- posix.mak | 6 ---- src/rt/osx_tls.c | 51 --------------------------- src/rt/sections_osx_x86_64.d | 67 ++++++++++++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 64 deletions(-) delete mode 100644 src/rt/osx_tls.c diff --git a/posix.mak b/posix.mak index cad8bc2bda..8ca675eb78 100644 --- a/posix.mak +++ b/posix.mak @@ -92,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,) 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)