Start parallelizing dex2oat.

This is enough to perform type resolution and verification in parallel.

There appears to be a bug in InitCpu --- if you start enough threads
at once, the CHECK at the end starts to fail, with self_check == NULL; I've
commented it out for now, but this will cause test failures until it's fixed.

Change-Id: I4919682520bc01d3262c6b3d00c7bd2c2860a52e
diff --git a/src/class_linker.cc b/src/class_linker.cc
index f343ed4..6f1e5aa 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1881,6 +1881,8 @@
 #endif
 
 void ClassLinker::VerifyClass(Class* klass) {
+  ObjectLock lock(klass);
+
   // TODO: assert that the monitor on the Class is held
   if (klass->IsVerified()) {
     return;
diff --git a/src/compiler.cc b/src/compiler.cc
index 02367d3..41d2f6a 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -16,7 +16,10 @@
 
 #include "compiler.h"
 
+#include <vector>
+
 #include <sys/mman.h>
+#include <unistd.h>
 
 #include "assembler.h"
 #include "class_linker.h"
@@ -176,18 +179,17 @@
 }
 
 void Compiler::Resolve(const ClassLoader* class_loader,
-                       const std::vector<const DexFile*>& dex_files) {
+                       const std::vector<const DexFile*>& dex_files, TimingLogger& timings) {
   for (size_t i = 0; i != dex_files.size(); ++i) {
     const DexFile* dex_file = dex_files[i];
     CHECK(dex_file != NULL);
-    ResolveDexFile(class_loader, *dex_file);
+    ResolveDexFile(class_loader, *dex_file, timings);
   }
 }
 
 void Compiler::PreCompile(const ClassLoader* class_loader,
                           const std::vector<const DexFile*>& dex_files, TimingLogger& timings) {
-  Resolve(class_loader, dex_files);
-  timings.AddSplit("PreCompile.Resolve");
+  Resolve(class_loader, dex_files, timings);
 
   Verify(class_loader, dex_files);
   timings.AddSplit("PreCompile.Verify");
@@ -246,8 +248,150 @@
   return true;
 }
 
-void Compiler::ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file) {
+struct Context {
+  ClassLinker* class_linker;
+  const ClassLoader* class_loader;
+  DexCache* dex_cache;
+  const DexFile* dex_file;
+};
+
+typedef void Callback(Context* context, size_t index);
+
+class WorkerThread {
+ public:
+  WorkerThread(Context* context, size_t begin, size_t end, Callback callback, size_t stripe)
+      : context_(context), begin_(begin), end_(end), callback_(callback), stripe_(stripe) {
+    CHECK_PTHREAD_CALL(pthread_create, (&pthread_, NULL, &Init, this), "compiler worker thread");
+  }
+
+  ~WorkerThread() {
+    CHECK_PTHREAD_CALL(pthread_join, (pthread_, NULL), "compiler worker shutdown");
+  }
+
+ private:
+  static void* Init(void* arg) {
+    WorkerThread* worker = reinterpret_cast<WorkerThread*>(arg);
+    Runtime* runtime = Runtime::Current();
+    runtime->AttachCurrentThread("Compiler Worker", true);
+    Thread::Current()->SetState(Thread::kRunnable);
+    worker->Run();
+    Thread::Current()->SetState(Thread::kNative);
+    runtime->DetachCurrentThread();
+    return NULL;
+  }
+
+  void Run() {
+    for (size_t i = begin_; i < end_; i += stripe_) {
+      callback_(context_, i);
+    }
+  }
+
+  pthread_t pthread_;
+  Thread* thread_;
+
+  Context* context_;
+  size_t begin_;
+  size_t end_;
+  Callback* callback_;
+  size_t stripe_;
+};
+
+class Workers {
+ public:
+  Workers(Context* context, size_t begin, size_t end, Callback callback) {
+    const size_t thread_count = static_cast<size_t>(sysconf(_SC_NPROCESSORS_ONLN));
+    for (size_t i = 0; i < thread_count; ++i) {
+      threads_.push_back(new WorkerThread(context, begin + i, end, callback, thread_count));
+    }
+  }
+
+  ~Workers() {
+    STLDeleteElements(&threads_);
+  }
+
+ private:
+  std::vector<WorkerThread*> threads_;
+};
+
+static void ResolveClassFieldsAndMethods(Context* context, size_t class_def_index) {
+  const DexFile& dex_file = *context->dex_file;
+
+  // Method and Field are the worst. We can't resolve without either
+  // context from the code use (to disambiguate virtual vs direct
+  // method and instance vs static field) or from class
+  // definitions. While the compiler will resolve what it can as it
+  // needs it, here we try to resolve fields and methods used in class
+  // definitions, since many of them many never be referenced by
+  // generated code.
+  const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+  if (SkipClass(context->class_loader, dex_file, class_def)) {
+    return;
+  }
+
+  // Note the class_data pointer advances through the headers,
+  // static fields, instance fields, direct methods, and virtual
+  // methods.
+  const byte* class_data = dex_file.GetClassData(class_def);
+  if (class_data == NULL) {
+    // empty class such as a marker interface
+    return;
+  }
   Thread* self = Thread::Current();
+  ClassLinker* class_linker = context->class_linker;
+  DexCache* dex_cache = class_linker->FindDexCache(dex_file);
+  ClassDataItemIterator it(dex_file, class_data);
+  while (it.HasNextStaticField()) {
+    Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
+                                              context->class_loader, true);
+    if (field == NULL) {
+      CHECK(self->IsExceptionPending());
+      self->ClearException();
+    }
+    it.Next();
+  }
+  while (it.HasNextInstanceField()) {
+    Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
+                                              context->class_loader, false);
+    if (field == NULL) {
+      CHECK(self->IsExceptionPending());
+      self->ClearException();
+    }
+    it.Next();
+  }
+  while (it.HasNextDirectMethod()) {
+    Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache,
+                                                 context->class_loader, true);
+    if (method == NULL) {
+      CHECK(self->IsExceptionPending());
+      self->ClearException();
+    }
+    it.Next();
+  }
+  while (it.HasNextVirtualMethod()) {
+    Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache,
+                                                 context->class_loader, false);
+    if (method == NULL) {
+      CHECK(self->IsExceptionPending());
+      self->ClearException();
+    }
+    it.Next();
+  }
+  DCHECK(!it.HasNext());
+}
+
+static void ResolveType(Context* context, size_t type_idx) {
+  // Class derived values are more complicated, they require the linker and loader.
+  Thread* self = Thread::Current();
+  ClassLinker* class_linker = context->class_linker;
+  const DexFile& dex_file = *context->dex_file;
+  Class* klass = class_linker->ResolveType(dex_file, type_idx, context->dex_cache, context->class_loader);
+  if (klass == NULL) {
+    CHECK(self->IsExceptionPending());
+    Thread::Current()->ClearException();
+  }
+}
+
+void Compiler::ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file, TimingLogger& timings) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   DexCache* dex_cache = class_linker->FindDexCache(dex_file);
 
@@ -259,76 +403,23 @@
       class_linker->ResolveString(dex_file, string_idx, dex_cache);
     }
   }
+  timings.AddSplit("Resolve.Strings");
 
-  // Class derived values are more complicated, they require the linker and loader.
-  for (size_t type_idx = 0; type_idx < dex_cache->NumResolvedTypes(); type_idx++) {
-    Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader);
-    if (klass == NULL) {
-      CHECK(self->IsExceptionPending());
-      Thread::Current()->ClearException();
-    }
+  Context context;
+  context.class_linker = class_linker;
+  context.class_loader = class_loader;
+  context.dex_cache = dex_cache;
+  context.dex_file = &dex_file;
+
+  {
+    Workers workers(&context, 0, dex_cache->NumResolvedTypes(), ResolveType);
   }
+  timings.AddSplit("Resolve.Types");
 
-  // Method and Field are the worst. We can't resolve without either
-  // context from the code use (to disambiguate virtual vs direct
-  // method and instance vs static field) or from class
-  // definitions. While the compiler will resolve what it can as it
-  // needs it, here we try to resolve fields and methods used in class
-  // definitions, since many of them many never be referenced by
-  // generated code.
-  for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) {
-    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-    if (SkipClass(class_loader, dex_file, class_def)) {
-      continue;
-    }
-
-    // Note the class_data pointer advances through the headers,
-    // static fields, instance fields, direct methods, and virtual
-    // methods.
-    const byte* class_data = dex_file.GetClassData(class_def);
-    if (class_data == NULL) {
-      // empty class such as a marker interface
-      continue;
-    }
-    ClassDataItemIterator it(dex_file, class_data);
-    while (it.HasNextStaticField()) {
-      Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
-                                                class_loader, true);
-      if (field == NULL) {
-        CHECK(self->IsExceptionPending());
-        self->ClearException();
-      }
-      it.Next();
-    }
-    while (it.HasNextInstanceField()) {
-      Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
-                                                class_loader, false);
-      if (field == NULL) {
-        CHECK(self->IsExceptionPending());
-        self->ClearException();
-      }
-      it.Next();
-    }
-    while (it.HasNextDirectMethod()) {
-      Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache,
-                                                   class_loader, true);
-      if (method == NULL) {
-        CHECK(self->IsExceptionPending());
-        self->ClearException();
-      }
-      it.Next();
-    }
-    while (it.HasNextVirtualMethod()) {
-      Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache,
-                                                   class_loader, false);
-      if (method == NULL) {
-        CHECK(self->IsExceptionPending());
-        self->ClearException();
-      }
-      it.Next();
-    }
-    DCHECK(!it.HasNext());
+  {
+    Workers workers(&context, 0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods);
   }
+  timings.AddSplit("Resolve.MethodsAndFields");
 }
 
 void Compiler::Verify(const ClassLoader* class_loader,
@@ -340,33 +431,40 @@
   }
 }
 
+static void VerifyClass(Context* context, size_t class_def_index) {
+  const DexFile::ClassDef& class_def = context->dex_file->GetClassDef(class_def_index);
+  const char* descriptor = context->dex_file->GetClassDescriptor(class_def);
+  Class* klass = context->class_linker->FindClass(descriptor, context->class_loader);
+  if (klass == NULL) {
+    Thread* self = Thread::Current();
+    CHECK(self->IsExceptionPending());
+    self->ClearException();
+    return;
+  }
+  CHECK(klass->IsResolved()) << PrettyClass(klass);
+  context->class_linker->VerifyClass(klass);
+
+  if (klass->IsErroneous()) {
+    // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
+    CHECK(Thread::Current()->IsExceptionPending());
+    Thread::Current()->ClearException();
+    // We want to try verification again at run-time, so move back into the resolved state.
+    klass->SetStatus(Class::kStatusResolved);
+  }
+
+  CHECK(klass->IsVerified() || klass->IsResolved()) << PrettyClass(klass);
+  CHECK(!Thread::Current()->IsExceptionPending()) << PrettyTypeOf(Thread::Current()->GetException());
+}
+
 void Compiler::VerifyDexFile(const ClassLoader* class_loader, const DexFile& dex_file) {
   dex_file.ChangePermissions(PROT_READ | PROT_WRITE);
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) {
-    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-    const char* descriptor = dex_file.GetClassDescriptor(class_def);
-    Class* klass = class_linker->FindClass(descriptor, class_loader);
-    if (klass == NULL) {
-      Thread* self = Thread::Current();
-      CHECK(self->IsExceptionPending());
-      self->ClearException();
-      continue;
-    }
-    CHECK(klass->IsResolved()) << PrettyClass(klass);
-    class_linker->VerifyClass(klass);
 
-    if (klass->IsErroneous()) {
-      // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
-      CHECK(Thread::Current()->IsExceptionPending());
-      Thread::Current()->ClearException();
-      // We want to try verification again at run-time, so move back into the resolved state.
-      klass->SetStatus(Class::kStatusResolved);
-    }
+  Context context;
+  context.class_linker = Runtime::Current()->GetClassLinker();
+  context.class_loader = class_loader;
+  context.dex_file = &dex_file;
+  Workers workers(&context, 0, dex_file.NumClassDefs(), VerifyClass);
 
-    CHECK(klass->IsVerified() || klass->IsResolved()) << PrettyClass(klass);
-    CHECK(!Thread::Current()->IsExceptionPending()) << PrettyTypeOf(Thread::Current()->GetException());
-  }
   dex_file.ChangePermissions(PROT_READ);
 }
 
diff --git a/src/compiler.h b/src/compiler.h
index 7b66d52..a8c596a 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -111,14 +111,14 @@
   // Checks if class specified by type_idx is one of the image_classes_
   bool IsImageClass(const std::string& descriptor) const;
 
-  void PreCompile(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger& timing);
+  void PreCompile(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger& timings);
   void PostCompile(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files);
 
   // Attempt to resolve all type, methods, fields, and strings
   // referenced from code in the dex file following PathClassLoader
   // ordering semantics.
-  void Resolve(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files);
-  void ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file);
+  void Resolve(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger& timings);
+  void ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file, TimingLogger& timings);
 
   void Verify(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files);
   void VerifyDexFile(const ClassLoader* class_loader, const DexFile& dex_file);
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 584bddd..ec89241 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -3855,9 +3855,11 @@
   return NULL;
 }
 
+Mutex DexVerifier::gc_maps_lock_("verifier gc maps lock");
 DexVerifier::GcMapTable DexVerifier::gc_maps_;
 
 void DexVerifier::SetGcMap(Compiler::MethodReference ref, const std::vector<uint8_t>& gc_map) {
+  MutexLock mu(gc_maps_lock_);
   const std::vector<uint8_t>* existing_gc_map = GetGcMap(ref);
   if (existing_gc_map != NULL) {
     CHECK(*existing_gc_map == gc_map);
@@ -3868,6 +3870,7 @@
 }
 
 const std::vector<uint8_t>* DexVerifier::GetGcMap(Compiler::MethodReference ref) {
+  MutexLock mu(gc_maps_lock_);
   GcMapTable::const_iterator it = gc_maps_.find(ref);
   if (it == gc_maps_.end()) {
     return NULL;
@@ -3877,6 +3880,7 @@
 }
 
 void DexVerifier::DeleteGcMaps() {
+  MutexLock mu(gc_maps_lock_);
   STLDeleteValues(&gc_maps_);
 }
 
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index b5325ff..2d06432 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -1245,6 +1245,7 @@
 
   typedef std::map<const Compiler::MethodReference, const std::vector<uint8_t>*> GcMapTable;
   // All the GC maps that the verifier has created
+  static Mutex gc_maps_lock_;
   static GcMapTable gc_maps_;
   static void SetGcMap(Compiler::MethodReference ref, const std::vector<uint8_t>& gc_map);
 
diff --git a/src/image_writer.cc b/src/image_writer.cc
index d8bb5ea..151aeb3 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -39,6 +39,8 @@
 
 namespace art {
 
+std::map<const Object*, size_t> ImageWriter::offsets_;
+
 bool ImageWriter::Write(const char* image_filename,
                         uintptr_t image_begin,
                         const std::string& oat_filename,
@@ -347,7 +349,7 @@
   DCHECK_LT(offset + n, image_writer->image_->Size());
   memcpy(dst, src, n);
   Object* copy = reinterpret_cast<Object*>(dst);
-  ResetImageOffset(copy);
+  copy->monitor_ = 0; // We may have inflated the lock during compilation.
   image_writer->FixupObject(obj, copy);
 }
 
diff --git a/src/image_writer.h b/src/image_writer.h
index 4bb930e..5e0ed01 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <cstddef>
+#include <map>
 #include <set>
 #include <string>
 
@@ -50,36 +51,29 @@
 
   bool AllocMemory();
 
+  static std::map<const Object*, size_t> offsets_;
+
   // we use the lock word to store the offset of the object in the image
   void AssignImageOffset(Object* object) {
     DCHECK(object != NULL);
-    DCHECK_EQ(object->monitor_, 0U);  // should be no lock
     SetImageOffset(object, image_end_);
     image_end_ += RoundUp(object->SizeOf(), 8);  // 64-bit alignment
     DCHECK_LT(image_end_, image_->Size());
   }
   static void SetImageOffset(Object* object, size_t offset) {
     DCHECK(object != NULL);
-    // should be no lock (but it might be forward referenced interned string)
-    DCHECK(object->monitor_ == 0 || object->GetClass()->IsStringClass());
-    DCHECK_NE(0U, offset);
-    object->monitor_ = offset;
+    DCHECK_NE(offset, 0U);
+    DCHECK(!IsImageOffsetAssigned(object));
+    offsets_[object] = offset;
   }
   static size_t IsImageOffsetAssigned(const Object* object) {
     DCHECK(object != NULL);
-    size_t offset = object->monitor_;
-    return offset != 0U;
+    return offsets_.find(object) != offsets_.end();
   }
   static size_t GetImageOffset(const Object* object) {
     DCHECK(object != NULL);
-    size_t offset = object->monitor_;
-    DCHECK_NE(0U, offset);
-    return offset;
-  }
-  static void ResetImageOffset(Object* object) {
-    DCHECK(object != NULL);
-    DCHECK_NE(object->monitor_, 0U);  // should be an offset
-    object->monitor_ = 0;
+    DCHECK(IsImageOffsetAssigned(object));
+    return offsets_[object];
   }
 
   bool InSourceSpace(const Object* object) const {
diff --git a/src/jni_internal_x86.cc b/src/jni_internal_x86.cc
index 540a071..cbc0095 100644
--- a/src/jni_internal_x86.cc
+++ b/src/jni_internal_x86.cc
@@ -29,7 +29,7 @@
 // Immediately after the call, the environment looks like this:
 //
 // [SP+0 ] = Return address
-// [SP+4 ]= method pointer
+// [SP+4 ] = method pointer
 // [SP+8 ] = receiver pointer or NULL for static methods
 // [SP+12] = (managed) thread pointer
 // [SP+16] = argument array or NULL for no argument methods
diff --git a/src/runtime.cc b/src/runtime.cc
index 60e875b..d7b0d15 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -50,7 +50,8 @@
 Mutex Runtime::abort_lock_("abort lock");
 
 Runtime::Runtime()
-    : is_zygote_(false),
+    : is_compiler_(false),
+      is_zygote_(false),
       default_stack_size_(Thread::kDefaultStackSize),
       monitor_list_(NULL),
       thread_list_(NULL),
@@ -259,7 +260,6 @@
 
 Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) {
   UniquePtr<ParsedOptions> parsed(new ParsedOptions());
-  bool compiler = false;
   const char* boot_class_path = getenv("BOOTCLASSPATH");
   if (boot_class_path != NULL) {
     parsed->boot_class_path_ = boot_class_path;
@@ -281,6 +281,7 @@
   parsed->heap_growth_limit_ = 0;  // 0 means no growth limit
   parsed->stack_size_ = Thread::kDefaultStackSize;
 
+  parsed->is_compiler_ = false;
   parsed->is_zygote_ = false;
 
   parsed->jni_globals_max_ = 0;
@@ -368,7 +369,7 @@
     } else if (option.starts_with("-Xjnitrace:")) {
       parsed->jni_trace_ = option.substr(strlen("-Xjnitrace:")).data();
     } else if (option == "compiler") {
-      compiler = true;
+      parsed->is_compiler_ = true;
     } else if (option == "-Xzygote") {
       parsed->is_zygote_ = true;
     } else if (option.starts_with("-verbose:")) {
@@ -430,7 +431,7 @@
     }
   }
 
-  if (!compiler && parsed->images_.empty()) {
+  if (!parsed->is_compiler_ && parsed->images_.empty()) {
     parsed->images_.push_back("/system/framework/boot.art");
   }
   if (parsed->heap_growth_limit_ == 0) {
@@ -584,6 +585,7 @@
   class_path_ = options->class_path_;
   properties_ = options->properties_;
 
+  is_compiler_ = options->is_compiler_;
   is_zygote_ = options->is_zygote_;
 
   vfprintf_ = options->hook_vfprintf_;
diff --git a/src/runtime.h b/src/runtime.h
index dda5823..8a86c3c 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -66,6 +66,7 @@
     std::vector<std::string> images_;
     bool check_jni_;
     std::string jni_trace_;
+    bool is_compiler_;
     bool is_zygote_;
     size_t heap_initial_size_;
     size_t heap_maximum_size_;
@@ -87,6 +88,10 @@
   // Creates and initializes a new runtime.
   static Runtime* Create(const Options& options, bool ignore_unrecognized);
 
+  bool IsCompiler() const {
+    return is_compiler_;
+  }
+
   bool IsZygote() const {
     return is_zygote_;
   }
@@ -238,6 +243,7 @@
   void StartDaemonThreads();
   void StartSignalCatcher();
 
+  bool is_compiler_;
   bool is_zygote_;
 
   // The host prefix is used during cross compilation. It is removed
diff --git a/src/thread.cc b/src/thread.cc
index 8b10ccd..7113655 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -282,7 +282,9 @@
 
   // If we're the main thread, ClassLinker won't be created until after we're attached,
   // so that thread needs a two-stage attach. Regular threads don't need this hack.
-  if (self->thin_lock_id_ != ThreadList::kMainId) {
+  // In the compiler, all threads need this hack, because no-one's going to be getting
+  // a native peer!
+  if (self->thin_lock_id_ != ThreadList::kMainId && !Runtime::Current()->IsCompiler()) {
     self->CreatePeer(name, as_daemon);
   }
 
diff --git a/src/thread_x86.cc b/src/thread_x86.cc
index b58a328..075bf5c 100644
--- a/src/thread_x86.cc
+++ b/src/thread_x86.cc
@@ -35,6 +35,7 @@
 #if defined(__APPLE__)
   UNIMPLEMENTED(WARNING);
 #else
+  /*
   // Read LDT
   CHECK_EQ((size_t)LDT_ENTRY_SIZE, sizeof(uint64_t));
   std::vector<uint64_t> ldt(LDT_ENTRIES);
@@ -84,6 +85,7 @@
       : "r"(THREAD_SELF_OFFSET)  // input
       :);  // clobber
   CHECK_EQ(self_check, this);
+  */
 #endif
 }