TLS for Android (and iOS)

Dan Olson at
Sun Mar 30 01:22:14 PDT 2014

"Joakim" <joakim at> writes:

> On Thursday, 27 March 2014 at 16:01:31 UTC, Dan Olson wrote:
>> If nobody is working on the emulated TLS for LDC, I will give it a
>> try.
>> Nothing to lose.
> Whatever I do to implement packed TLS in the dmd backend is not going
> to work for ldc anyway, so nothing stopping you from making your own
> effort.  You will have to patch llvm also, if the weak symbols bug
> David pointed out is still around in llvm 3.5.  Let us know what
> approach you take.

The approach I started with was to make LLVM do the work.  I read
through all of the comments in this thread and decided this might be the
most fun.

ARMISelLowering.cpp has TLS disabled for all but ELF targets.  I
commented out an assertion blocking other targets to see what would
happen for iOS (Mach-O).  To my suprise, found that Mach-O tls sections
are generated (__thread_vars, __thread_data, .tbss) and populated with
the D thread local vars.

The load/store instructions were treating TLS vars like global data
though.  So I looked at the Mach-O X86 version and saw what it is trying
to do.  LLVM coding is still a mystery to me, but managed after many
hours today to hack together something that would turn this D code

module tlsd;
int a;

void test()
  a += 4;   // access a

into this:

	movw	r0, :lower16:(__D4tlsd1ai-(LPC4_0+4))
	movt	r0, :upper16:(__D4tlsd1ai-(LPC4_0+4))
	add	r0, pc
	blx	___tls_get_addr
	ldr	r1, [r0]
	adds	r1, #4
	str	r1, [r0]


.tbss __D4tlsd1ai$tlv$init, 4, 2

	.section	__DATA,__thread_vars,thread_local_variables
	.globl	__D4tlsd1ai
	.long	__tlv_bootstrap
	.long	0
	.long	__D4tlsd1ai$tlv$init

The following link helped explain what is going on with the
__thread_vars data layout.

Mach-O dyln replaces tlv_bootstrap (thunk) with tlv_get_addr in the
TLVDescriptor (__thread_vars).  My LLVM hack for now is just doing a
direct call to __tls_get_addr instead of indirect to tlv_get_addr.  For
proof of concept (one thread only), I have __tls_get_addr hard wired as

extern (C)
    struct TLVDescriptor
	void*  function(TLVDescriptor*) thunk;
	uint	key;
	uint	offset;

    //void* tlv_get_addr(TLVDescriptor* d)
    //void* __tls_get_addr(void* ptr)
    void* __tls_get_addr(TLVDescriptor* tlvd)
        __gshared static ubyte data[512];

        printf("__tls_get_addr %p \n", tlvd);
        printf("thunk %p, key %u, offset %u\n",
               tlvd.thunk, tlvd.key, tlvd.offset);
        return data.ptr + tlvd.offset;

    void _tlv_bootstrap()
        assert(false, "Should not get here");

It looks promising.  Next step is to add in some realistic runtime
support.  Not sure if I will base it on dmd's sections-osx or the Apple
dyld.  Probably a hybrid.

Eventually will need some help getting the LLVM changes clean instead of
my hack job.

Now that I've gone down this path a bit, I am beginning to wonder if
changing LLVM to support iOS thread locals will have issues.  Would LLVM
want changes that affect Darwin/Mach-O (Apple's turf)?  I suppose they
could be optional.

More information about the digitalmars-d-ldc mailing list