Implement tlsglobals support on macOS 58/4958/10
authorEvan Ramos <evan@hpccharm.com>
Mon, 18 Feb 2019 19:41:16 +0000 (13:41 -0600)
committerEvan Ramos <evan@hpccharm.com>
Thu, 28 Feb 2019 23:07:33 +0000 (17:07 -0600)
Change-Id: Ic35df8a8712e91a5522014b30fc5afbeed4814bd

doc/ampi/manual.rst
doc/ampi/manual.tex
src/scripts/charmc
src/scripts/configure.ac
src/util/cmitls.C
tests/ampi/privatization/Makefile

index 43c2fea..71a47b1 100644 (file)
@@ -372,8 +372,7 @@ by annotating global/static variable declarations in C with
 attribute. OpenMP is required for using tlsglobals in Fortran code since
 Fortran has no other method of using TLS. The *__thread* keyword is not
 an official extension of the C language, though compiler writers are
-encouraged to implement this feature. Currently, the ELF object file
-format supports Thread Local Storage.
+encouraged to implement this feature.
 
 It handles both global and static variables and has no context-switching
 overhead. AMPI provides runtime support for privatizing thread-local
@@ -634,7 +633,7 @@ different schemes.
    ==================== === ====== ====== ==== ======= ===== =====
    Transformation       Yes Yes    Yes    Yes  Yes     Yes   Yes
    GOT-Globals          Yes Yes    No     No   No      Yes   Yes
-   TLS-Globals          Yes Yes    No     No   Maybe   Maybe Maybe
+   TLS-Globals          Yes Yes    Yes    No   Maybe   Maybe Maybe
    ==================== === ====== ====== ==== ======= ===== =====
 
 Extensions for Migrations
index 71ad0da..6a11344 100644 (file)
@@ -339,7 +339,7 @@ or C11 with \emph{thread\_local} or \emph{\_Thread\_local},
 and in Fortran with OpenMP's \emph{threadprivate} attribute. OpenMP is required for using tlsglobals
 in Fortran code since Fortran has no other method of using TLS. The \emph{\_\_thread} keyword is
 not an official extension of the C language, though compiler writers are encouraged to implement
-this feature. Currently, the ELF object file format supports Thread Local Storage.
+this feature.
 
 It handles both global and static variables and has no context-switching
 overhead. AMPI provides runtime support for privatizing thread-local variables to user-level threads
@@ -563,7 +563,7 @@ Transformation           & Yes     & Yes     & Yes     & Yes   & Yes     & Yes
 \hline
 GOT-Globals              & Yes     & Yes     & No      & No    & No      & Yes   & Yes   \\
 \hline
-TLS-Globals              & Yes     & Yes     & No      & No    & Maybe   & Maybe & Maybe \\
+TLS-Globals              & Yes     & Yes     & Yes     & No    & Maybe   & Maybe & Maybe \\
 \hline
 \end{tabular}
 \caption{Portability of current implementations of three privatization schemes.
index 3bc5097..2198cac 100755 (executable)
@@ -1018,9 +1018,11 @@ fi
 
 if [ "$TLSGLOBALS" = "1" ]
 then
-    OPTS_CC="$OPTS_CC -mno-tls-direct-seg-refs -ftls-model=initial-exec"
-    OPTS_CXX="$OPTS_CXX -mno-tls-direct-seg-refs -ftls-model=initial-exec"
-    OPTS_F90="$OPTS_F90 -mno-tls-direct-seg-refs -ftls-model=initial-exec"
+    TLSGLOBALS_OPTS='-ftls-model=initial-exec'
+    [ "$CMK_COMPILER_KNOWS_TLSDIRECTSEGREFS" = '1' ] && TLSGLOBALS_OPTS="$TLSGLOBALS_OPTS -mno-tls-direct-seg-refs"
+    OPTS_CC="$OPTS_CC $TLSGLOBALS_OPTS"
+    OPTS_CXX="$OPTS_CXX $TLSGLOBALS_OPTS"
+    OPTS_F90="$OPTS_F90 $TLSGLOBALS_OPTS"
 fi
 
 # Look up and add the dependencies for module $1
@@ -1383,13 +1385,13 @@ then
       echo "Error: -tlsglobals is not supported with the uth machine layer."
       exit 1
     fi
-    if ! `$CMK_CC --version | grep -q gcc`
+    if [ "$CMK_COMPILER_KNOWS_TLSDIRECTSEGREFS" != '1' ] && [ "$CMK_COMPILER" != 'clang' -o "$CMK_MACOSX" != '1' ]
     then
-      echo "Warning: -tlsglobals may not work with compilers other than GCC."
+      echo "Warning: -tlsglobals may not work with compilers that do not recognize -mno-tls-direct-seg-refs."
     fi
-    if ! `$CMK_LD -Wl,--version,-v 2>&1 | grep -q elf`
+    if [ "$CMK_HAS_ELF_H" != '1' -a "$CMK_MACOSX" != '1' ]
     then
-      echo "Warning: -tlsglobals may not work with a linker that does not report ELF support."
+      echo "Warning: -tlsglobals requires elf.h"
     fi
 fi
 
@@ -1524,11 +1526,7 @@ then
 fi
 if [ "$TLSGLOBALS"  = "1" ]
 then
-    # make sure we don't add an extra -tls
-  if ! echo $THREAD | grep '\-tls$' > /dev/null 2> /dev/null
-  then
-    THREAD=${THREAD}-tls
-  fi
+  THREAD=${THREAD%-tls}-tls
 fi
 
 THREAD_OBJ="-lthreads-${THREAD}"
index e545a31..6eb50f6 100644 (file)
@@ -782,6 +782,13 @@ then
     OPTS_CXX="$OPTS_CXX -fno-lifetime-dse"
 fi
 
+# Test for a flag tlsglobals sometimes depends on
+test_cxx "whether C++ compiler accepts -mno-tls-direct-seg-refs" "yes" "no" "-mno-tls-direct-seg-refs"
+if test $strictpass -eq 1
+then
+    add_flag 'CMK_COMPILER_KNOWS_TLSDIRECTSEGREFS="1"' "tlsglobals"
+fi
+
 # Determine compiler/linker flags to build libcharm.so for charm4py
 if test "$enable_charmpy" = "yes"
 then
index f6a84c3..cfa2c42 100644 (file)
@@ -38,7 +38,11 @@ void* swapTLS(void*);
 
 #if CMK_TLS_SWITCHING_X86_64
 # define CMK_TLS_X86_MOV "movq"
-# define CMK_TLS_X86_REG "fs"
+# ifdef __APPLE__
+#  define CMK_TLS_X86_REG "gs"
+# else
+#  define CMK_TLS_X86_REG "fs"
+# endif
 #elif CMK_TLS_SWITCHING_X86
 # define CMK_TLS_X86_MOV "movl"
 # define CMK_TLS_X86_REG "gs"
@@ -111,6 +115,147 @@ static void populateTLSSegStats(tlsseg_t * t)
   dl_iterate_phdr(count_tls_sizes, t); /* count all PT_TLS sections */
 }
 
+#elif defined __APPLE__
+
+// parts adapted from threadLocalVariables.c in dyld
+
+# include <mach-o/dyld.h>
+# include <mach-o/nlist.h>
+
+#if __LP64__
+  typedef struct mach_header_64 macho_header;
+# define MACHO_HEADER_MAGIC MH_MAGIC_64
+# define LC_SEGMENT_COMMAND LC_SEGMENT_64
+  typedef struct segment_command_64 macho_segment_command;
+  typedef struct section_64 macho_section;
+  typedef struct nlist_64 macho_nlist;
+#else
+  typedef struct mach_header macho_header;
+# define MACHO_HEADER_MAGIC MH_MAGIC
+# define LC_SEGMENT_COMMAND LC_SEGMENT
+  typedef struct segment_command macho_segment_command;
+  typedef struct section macho_section;
+  typedef struct nlist macho_nlist;
+#endif
+
+static size_t GetTLVSizeFromMachOHeader()
+{
+  size_t totalsize = 0;
+
+  for (uint32_t c = 0; c < _dyld_image_count(); ++c)
+  {
+    const struct mach_header * const mh_orig = _dyld_get_image_header(c);
+    if (mh_orig == nullptr)
+      continue;
+
+    CmiEnforce(mh_orig->magic == MACHO_HEADER_MAGIC);
+    const auto mh = (const macho_header *)mh_orig;
+    const auto mh_addr = (const char *)mh;
+
+    const uint8_t * start = nullptr;
+    unsigned long size = 0;
+    intptr_t slide = 0;
+    bool slideComputed = false;
+
+    uint64_t text_vmaddr = 0, linkedit_vmaddr = 0, linkedit_fileoff = 0;
+
+    const uint32_t cmd_count = mh->ncmds;
+    const auto cmds = (const struct load_command *)(mh_addr + sizeof(macho_header));
+    const struct load_command * cmd = cmds;
+    for (uint32_t i = 0; i < cmd_count; ++i)
+    {
+      const auto lc_type = cmd->cmd & ~LC_REQ_DYLD;
+      if (lc_type == LC_SEGMENT_COMMAND)
+      {
+        const auto seg = (const macho_segment_command *)cmd;
+
+        if (!slideComputed && seg->filesize != 0)
+        {
+          slide = (uintptr_t)mh - seg->vmaddr;
+          slideComputed = true;
+        }
+
+        if (strcmp(seg->segname, SEG_TEXT) == 0)
+          text_vmaddr = seg->vmaddr;
+        else if (strcmp(seg->segname, SEG_LINKEDIT) == 0)
+        {
+          linkedit_vmaddr = seg->vmaddr;
+          linkedit_fileoff = seg->fileoff;
+        }
+
+        // look for TLV sections, used by Apple's clang
+
+        const auto sectionsStart = (const macho_section *)((const char *)seg + sizeof(macho_segment_command));
+        const auto sectionsEnd = sectionsStart + seg->nsects;
+        for (auto sect = sectionsStart; sect < sectionsEnd; ++sect)
+        {
+          const auto section_type = sect->flags & SECTION_TYPE;
+          if (section_type == S_THREAD_LOCAL_ZEROFILL || section_type == S_THREAD_LOCAL_REGULAR)
+          {
+            if (start == nullptr)
+            {
+              // first of N contiguous TLV template sections: record as if this were the only section
+              start = (const uint8_t *)(sect->addr + slide);
+              size = sect->size;
+            }
+            else
+            {
+              // non-first of N contiguous TLV template sections: accumulate values
+              const auto newEnd = (const uint8_t *)(sect->addr + slide + sect->size);
+              size = newEnd - start;
+            }
+          }
+        }
+      }
+      else if (lc_type == LC_SYMTAB && text_vmaddr)
+      {
+        // look through all symbols for GNU emutls, used by GCC
+
+        const auto symcmd = (const struct symtab_command *)cmd;
+        auto strtab = (const char *)(linkedit_vmaddr + (symcmd->stroff - linkedit_fileoff) + slide);
+        auto symtab = (const macho_nlist *)(linkedit_vmaddr + (symcmd->symoff - linkedit_fileoff) + slide);
+
+        for (const macho_nlist * nl = symtab, * const nl_end = symtab + symcmd->nsyms; nl < nl_end; ++nl)
+        {
+          if ((nl->n_type & (N_TYPE & N_SECT)) != N_SECT)
+            continue;
+
+          static const char emutls_prefix[] = "___emutls_v.";
+          const char * const symname = strtab + nl->n_un.n_strx;
+          if (strncmp(symname, emutls_prefix, sizeof(emutls_prefix)-1) == 0)
+          {
+            const auto addr = (const size_t *)(nl->n_value + slide);
+            const size_t symsize = *addr;
+            totalsize += symsize;
+          }
+        }
+      }
+
+      cmd = (const struct load_command *)(((const char *)cmd) + cmd->cmdsize);
+    }
+
+    totalsize += size;
+  }
+
+  return totalsize;
+}
+
+static size_t CmiTLSSize;
+
+static inline void CmiTLSStatsInit(void)
+{
+  // calculate the TLS size once at startup
+  CmiTLSSize = GetTLVSizeFromMachOHeader();
+}
+
+static void populateTLSSegStats(tlsseg_t * t)
+{
+  // fill the struct with the cached size
+  t->size = CmiTLSSize;
+  // Apple uses alignment by 16
+  t->align = 16;
+}
+
 #elif CMK_HAS_ELF_H && CMK_DLL_USE_DLOPEN && CMK_HAS_RTLD_DEFAULT
 
 # include <dlfcn.h>
index 493e7ea..6963106 100644 (file)
@@ -23,14 +23,10 @@ CANDIDATES := \
 
 
 # Define what options to pass to charmc for each method.
+$(foreach i,$(VARIANTS),$(foreach j,$(LANGUAGES),$(eval OPTS_$j_$i := -$i)))
 OPTS_cxx_control :=
-OPTS_cxx_swapglobals := -swapglobals
-OPTS_cxx_tlsglobals := -tlsglobals
-OPTS_cxx_roseomptlsglobals := -roseomptlsglobals
 OPTS_f90_control :=
-OPTS_f90_swapglobals := -swapglobals
-OPTS_f90_tlsglobals := -tlsglobals -fopenmp
-OPTS_f90_roseomptlsglobals := -roseomptlsglobals
+OPTS_f90_tlsglobals += -fopenmp
 
 # Define the features that each privatization method supports.
 FEATURES_cxx_control := staticvars sharedlib dynamiclib