Merge "Support for inlining virtual and interface calls."
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 11743f3..32bde8e 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -718,6 +718,9 @@
     // contains data only valid during a real run.
     dex_cache->SetFieldObject<false>(mirror::DexCache::DexOffset(), nullptr);
   }
+
+  // Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
+  class_linker->DropFindArrayClassCache();
 }
 
 bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) {
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index f98029d..dbda63b 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -763,6 +763,11 @@
   }
   DCHECK_EQ(argument_index, number_of_arguments);
 
+  if (invoke->IsInvokeStaticOrDirect()) {
+    invoke->SetArgumentAt(argument_index, graph_->GetCurrentMethod());
+    argument_index++;
+  }
+
   if (clinit_check_requirement == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit) {
     // Add the class initialization check as last input of `invoke`.
     DCHECK(clinit_check != nullptr);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 08c0351..049b3e3 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -292,7 +292,6 @@
     HInvoke* invoke, InvokeDexCallingConventionVisitor* visitor) {
   ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
   LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kCall);
-  locations->AddTemp(visitor->GetMethodLocation());
 
   for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) {
     HInstruction* input = invoke->InputAt(i);
@@ -300,6 +299,20 @@
   }
 
   locations->SetOut(visitor->GetReturnLocation(invoke->GetType()));
+
+  if (invoke->IsInvokeStaticOrDirect()) {
+    HInvokeStaticOrDirect* call = invoke->AsInvokeStaticOrDirect();
+    if (call->IsStringInit()) {
+      locations->AddTemp(visitor->GetMethodLocation());
+    } else if (call->IsRecursive()) {
+      locations->SetInAt(call->GetCurrentMethodInputIndex(), visitor->GetMethodLocation());
+    } else {
+      locations->AddTemp(visitor->GetMethodLocation());
+      locations->SetInAt(call->GetCurrentMethodInputIndex(), Location::RequiresRegister());
+    }
+  } else {
+    locations->AddTemp(visitor->GetMethodLocation());
+  }
 }
 
 void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const {
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 04952be..f4544ea 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1254,6 +1254,10 @@
   IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(),
                                          codegen_->GetInstructionSetFeatures());
   if (intrinsic.TryDispatch(invoke)) {
+    LocationSummary* locations = invoke->GetLocations();
+    if (locations->CanCall()) {
+      locations->SetInAt(invoke->GetCurrentMethodInputIndex(), Location::RequiresRegister());
+    }
     return;
   }
 
@@ -1283,9 +1287,9 @@
     return;
   }
 
-  Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
-
-  codegen_->GenerateStaticOrDirectCall(invoke, temp);
+  LocationSummary* locations = invoke->GetLocations();
+  codegen_->GenerateStaticOrDirectCall(
+      invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
 
@@ -1316,12 +1320,8 @@
   Location receiver = locations->InAt(0);
   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
   // temp = object->GetClass();
-  if (receiver.IsStackSlot()) {
-    __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex());
-    __ LoadFromOffset(kLoadWord, temp, temp, class_offset);
-  } else {
-    __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
-  }
+  DCHECK(receiver.IsRegister());
+  __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
   codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetMethodAt(method_offset);
   uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
@@ -1638,8 +1638,7 @@
           // Processing a Dex `long-to-double' instruction.
           locations->SetInAt(0, Location::RequiresRegister());
           locations->SetOut(Location::RequiresFpuRegister());
-          locations->AddTemp(Location::RequiresRegister());
-          locations->AddTemp(Location::RequiresRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
           locations->AddTemp(Location::RequiresFpuRegister());
           break;
 
@@ -1857,29 +1856,21 @@
           Register high = in.AsRegisterPairHigh<Register>();
           SRegister out_s = out.AsFpuRegisterPairLow<SRegister>();
           DRegister out_d = FromLowSToD(out_s);
-          Register constant_low = locations->GetTemp(0).AsRegister<Register>();
-          Register constant_high = locations->GetTemp(1).AsRegister<Register>();
-          SRegister temp_s = locations->GetTemp(2).AsFpuRegisterPairLow<SRegister>();
+          SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
           DRegister temp_d = FromLowSToD(temp_s);
+          SRegister constant_s = locations->GetTemp(1).AsFpuRegisterPairLow<SRegister>();
+          DRegister constant_d = FromLowSToD(constant_s);
 
-          // out_d = int-to-double(high)
-          __ vmovsr(out_s, high);
-          __ vcvtdi(out_d, out_s);
-          // Using vmovd to load the `k2Pow32EncodingForDouble` constant
-          // as an immediate value into `temp_d` does not work, as
-          // this instruction only transfers 8 significant bits of its
-          // immediate operand.  Instead, use two 32-bit core
-          // registers to load `k2Pow32EncodingForDouble` into `temp_d`.
-          __ LoadImmediate(constant_low, Low32Bits(k2Pow32EncodingForDouble));
-          __ LoadImmediate(constant_high, High32Bits(k2Pow32EncodingForDouble));
-          __ vmovdrr(temp_d, constant_low, constant_high);
-          // out_d = out_d * 2^32
-          __ vmuld(out_d, out_d, temp_d);
-          // temp_d = unsigned-to-double(low)
-          __ vmovsr(temp_s, low);
-          __ vcvtdu(temp_d, temp_s);
-          // out_d = out_d + temp_d
-          __ vaddd(out_d, out_d, temp_d);
+          // temp_d = int-to-double(high)
+          __ vmovsr(temp_s, high);
+          __ vcvtdi(temp_d, temp_s);
+          // constant_d = k2Pow32EncodingForDouble
+          __ LoadDImmediate(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
+          // out_d = unsigned-to-double(low)
+          __ vmovsr(out_s, low);
+          __ vcvtdu(out_d, out_s);
+          // out_d += temp_d * constant_d
+          __ vmlad(out_d, temp_d, constant_d);
           break;
         }
 
@@ -2910,22 +2901,22 @@
 
 void InstructionCodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) {
   // TODO (ported from quick): revisit Arm barrier kinds
-  DmbOptions flavour = DmbOptions::ISH;  // quiet c++ warnings
+  DmbOptions flavor = DmbOptions::ISH;  // quiet c++ warnings
   switch (kind) {
     case MemBarrierKind::kAnyStore:
     case MemBarrierKind::kLoadAny:
     case MemBarrierKind::kAnyAny: {
-      flavour = DmbOptions::ISH;
+      flavor = DmbOptions::ISH;
       break;
     }
     case MemBarrierKind::kStoreStore: {
-      flavour = DmbOptions::ISHST;
+      flavor = DmbOptions::ISHST;
       break;
     }
     default:
       LOG(FATAL) << "Unexpected memory barrier " << kind;
   }
-  __ dmb(flavour);
+  __ dmb(flavor);
 }
 
 void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr,
@@ -4215,9 +4206,7 @@
   }
 }
 
-void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp) {
-  DCHECK_EQ(temp, kArtMethodRegister);
-
+void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
   // TODO: Implement all kinds of calls:
   // 1) boot -> boot
   // 2) app -> boot
@@ -4226,32 +4215,32 @@
   // Currently we implement the app -> app logic, which looks up in the resolve cache.
 
   if (invoke->IsStringInit()) {
+    Register reg = temp.AsRegister<Register>();
     // temp = thread->string_init_entrypoint
-    __ LoadFromOffset(kLoadWord, temp, TR, invoke->GetStringInitOffset());
+    __ LoadFromOffset(kLoadWord, reg, TR, invoke->GetStringInitOffset());
     // LR = temp[offset_of_quick_compiled_code]
-    __ LoadFromOffset(kLoadWord, LR, temp,
+    __ LoadFromOffset(kLoadWord, LR, reg,
                       ArtMethod::EntryPointFromQuickCompiledCodeOffset(
                           kArmWordSize).Int32Value());
     // LR()
     __ blx(LR);
+  } else if (invoke->IsRecursive()) {
+    __ bl(GetFrameEntryLabel());
   } else {
-    // temp = method;
-    LoadCurrentMethod(temp);
-    if (!invoke->IsRecursive()) {
-      // temp = temp->dex_cache_resolved_methods_;
-      __ LoadFromOffset(
-          kLoadWord, temp, temp, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
-      // temp = temp[index_in_cache]
-      __ LoadFromOffset(
-          kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()));
-      // LR = temp[offset_of_quick_compiled_code]
-      __ LoadFromOffset(kLoadWord, LR, temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-          kArmWordSize).Int32Value());
-      // LR()
-      __ blx(LR);
-    } else {
-      __ bl(GetFrameEntryLabel());
-    }
+    Register current_method =
+        invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex()).AsRegister<Register>();
+    Register reg = temp.AsRegister<Register>();
+    // reg = current_method->dex_cache_resolved_methods_;
+    __ LoadFromOffset(
+        kLoadWord, reg, current_method, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+    // reg = reg[index_in_cache]
+    __ LoadFromOffset(
+        kLoadWord, reg, reg, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()));
+    // LR = reg[offset_of_quick_compiled_code]
+    __ LoadFromOffset(kLoadWord, LR, reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+        kArmWordSize).Int32Value());
+    // LR()
+    __ blx(LR);
   }
 
   DCHECK(!IsLeafMethod());
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index d84f2d3..b871acd 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -301,7 +301,7 @@
 
   Label* GetFrameEntryLabel() { return &frame_entry_label_; }
 
-  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp);
+  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
 
  private:
   // Labels for each block that will be compiled.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 2f607f7..ac99d56 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -484,7 +484,7 @@
 }
 
 Location InvokeDexCallingConventionVisitorARM64::GetMethodLocation() const {
-  return LocationFrom(x0);
+  return LocationFrom(kArtMethodRegister);
 }
 
 CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph,
@@ -2227,6 +2227,10 @@
 
   IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena());
   if (intrinsic.TryDispatch(invoke)) {
+    LocationSummary* locations = invoke->GetLocations();
+    if (locations->CanCall()) {
+      locations->SetInAt(invoke->GetCurrentMethodInputIndex(), Location::RequiresRegister());
+    }
     return;
   }
 
@@ -2242,9 +2246,8 @@
   return false;
 }
 
-void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp) {
+void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
   // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
-  DCHECK(temp.Is(kArtMethodRegister));
   size_t index_in_cache = GetCachePointerOffset(invoke->GetDexMethodIndex());
 
   // TODO: Implement all kinds of calls:
@@ -2255,30 +2258,30 @@
   // Currently we implement the app -> app logic, which looks up in the resolve cache.
 
   if (invoke->IsStringInit()) {
+    Register reg = XRegisterFrom(temp);
     // temp = thread->string_init_entrypoint
-    __ Ldr(temp.X(), MemOperand(tr, invoke->GetStringInitOffset()));
+    __ Ldr(reg.X(), MemOperand(tr, invoke->GetStringInitOffset()));
     // LR = temp->entry_point_from_quick_compiled_code_;
     __ Ldr(lr, MemOperand(
-        temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize).Int32Value()));
+        reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize).Int32Value()));
     // lr()
     __ Blr(lr);
+  } else if (invoke->IsRecursive()) {
+    __ Bl(&frame_entry_label_);
   } else {
-    // temp = method;
-    LoadCurrentMethod(temp.X());
-    if (!invoke->IsRecursive()) {
-      // temp = temp->dex_cache_resolved_methods_;
-      __ Ldr(temp.W(), MemOperand(temp.X(),
-                                  ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
-      // temp = temp[index_in_cache];
-      __ Ldr(temp.X(), MemOperand(temp, index_in_cache));
-      // lr = temp->entry_point_from_quick_compiled_code_;
-      __ Ldr(lr, MemOperand(temp.X(), ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-          kArm64WordSize).Int32Value()));
-      // lr();
-      __ Blr(lr);
-    } else {
-      __ Bl(&frame_entry_label_);
-    }
+    Register current_method =
+        XRegisterFrom(invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex()));
+    Register reg = XRegisterFrom(temp);
+    // temp = current_method->dex_cache_resolved_methods_;
+    __ Ldr(reg.W(), MemOperand(current_method.X(),
+                               ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+    // temp = temp[index_in_cache];
+    __ Ldr(reg.X(), MemOperand(reg, index_in_cache));
+    // lr = temp->entry_point_from_quick_compiled_code_;
+    __ Ldr(lr, MemOperand(reg.X(), ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+        kArm64WordSize).Int32Value()));
+    // lr();
+    __ Blr(lr);
   }
 
   DCHECK(!IsLeafMethod());
@@ -2294,8 +2297,9 @@
   }
 
   BlockPoolsScope block_pools(GetVIXLAssembler());
-  Register temp = XRegisterFrom(invoke->GetLocations()->GetTemp(0));
-  codegen_->GenerateStaticOrDirectCall(invoke, temp);
+  LocationSummary* locations = invoke->GetLocations();
+  codegen_->GenerateStaticOrDirectCall(
+      invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
 
@@ -2314,14 +2318,8 @@
 
   BlockPoolsScope block_pools(GetVIXLAssembler());
 
-  // temp = object->GetClass();
-  if (receiver.IsStackSlot()) {
-    __ Ldr(temp.W(), MemOperand(sp, receiver.GetStackIndex()));
-    __ Ldr(temp.W(), HeapOperand(temp.W(), class_offset));
-  } else {
-    DCHECK(receiver.IsRegister());
-    __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
-  }
+  DCHECK(receiver.IsRegister());
+  __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
   codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetMethodAt(method_offset);
   __ Ldr(temp, MemOperand(temp, method_offset));
@@ -2674,7 +2672,7 @@
 void LocationsBuilderARM64::VisitCurrentMethod(HCurrentMethod* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetOut(LocationFrom(x0));
+  locations->SetOut(LocationFrom(kArtMethodRegister));
 }
 
 void InstructionCodeGeneratorARM64::VisitCurrentMethod(
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index c62ba95..3246648 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -344,7 +344,7 @@
     return false;
   }
 
-  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, vixl::Register temp);
+  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
 
  private:
   // Labels for each block that will be compiled.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 8a7b52e..4065c44 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1231,6 +1231,10 @@
 
   IntrinsicLocationsBuilderX86 intrinsic(codegen_);
   if (intrinsic.TryDispatch(invoke)) {
+    LocationSummary* locations = invoke->GetLocations();
+    if (locations->CanCall()) {
+      locations->SetInAt(invoke->GetCurrentMethodInputIndex(), Location::RequiresRegister());
+    }
     return;
   }
 
@@ -1255,8 +1259,9 @@
     return;
   }
 
+  LocationSummary* locations = invoke->GetLocations();
   codegen_->GenerateStaticOrDirectCall(
-      invoke, invoke->GetLocations()->GetTemp(0).AsRegister<Register>());
+      invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
 
@@ -1276,13 +1281,8 @@
   LocationSummary* locations = invoke->GetLocations();
   Location receiver = locations->InAt(0);
   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  // temp = object->GetClass();
-  if (receiver.IsStackSlot()) {
-    __ movl(temp, Address(ESP, receiver.GetStackIndex()));
-    __ movl(temp, Address(temp, class_offset));
-  } else {
-    __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
-  }
+  DCHECK(receiver.IsRegister());
+  __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
   codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetMethodAt(method_offset);
   __ movl(temp, Address(temp, method_offset));
@@ -3201,7 +3201,7 @@
 
 
 void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
-                                                  Register temp) {
+                                                  Location temp) {
   // TODO: Implement all kinds of calls:
   // 1) boot -> boot
   // 2) app -> boot
@@ -3211,25 +3211,26 @@
 
   if (invoke->IsStringInit()) {
     // temp = thread->string_init_entrypoint
-    __ fs()->movl(temp, Address::Absolute(invoke->GetStringInitOffset()));
+    Register reg = temp.AsRegister<Register>();
+    __ fs()->movl(reg, Address::Absolute(invoke->GetStringInitOffset()));
     // (temp + offset_of_quick_compiled_code)()
     __ call(Address(
-        temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+        reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+  } else if (invoke->IsRecursive()) {
+    __ call(GetFrameEntryLabel());
   } else {
-    // temp = method;
-    LoadCurrentMethod(temp);
-    if (!invoke->IsRecursive()) {
-      // temp = temp->dex_cache_resolved_methods_;
-      __ movl(temp, Address(temp, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
-      // temp = temp[index_in_cache]
-      __ movl(temp, Address(temp,
-                            CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())));
-      // (temp + offset_of_quick_compiled_code)()
-      __ call(Address(temp,
-          ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
-    } else {
-      __ call(GetFrameEntryLabel());
-    }
+    Register current_method =
+        invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex()).AsRegister<Register>();
+    Register reg = temp.AsRegister<Register>();
+    // temp = temp->dex_cache_resolved_methods_;
+    __ movl(reg, Address(
+        current_method, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+    // temp = temp[index_in_cache]
+    __ movl(reg, Address(reg,
+                         CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())));
+    // (temp + offset_of_quick_compiled_code)()
+    __ call(Address(reg,
+        ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
   }
 
   DCHECK(!IsLeafMethod());
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 61827a4..b8553d2 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -263,7 +263,7 @@
   void Move64(Location destination, Location source);
 
   // Generate a call to a static or direct method.
-  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp);
+  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
 
   // Emit a write barrier.
   void MarkGCCard(Register temp,
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index a2a3cf5..c9fe813 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -360,7 +360,7 @@
 }
 
 void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
-                                                     CpuRegister temp) {
+                                                     Location temp) {
   // All registers are assumed to be correctly set up.
 
   // TODO: Implement all kinds of calls:
@@ -371,26 +371,28 @@
   // Currently we implement the app -> app logic, which looks up in the resolve cache.
 
   if (invoke->IsStringInit()) {
+    CpuRegister reg = temp.AsRegister<CpuRegister>();
     // temp = thread->string_init_entrypoint
-    __ gs()->movl(temp, Address::Absolute(invoke->GetStringInitOffset()));
+    __ gs()->movl(reg, Address::Absolute(invoke->GetStringInitOffset()));
     // (temp + offset_of_quick_compiled_code)()
-    __ call(Address(temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+    __ call(Address(reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
         kX86_64WordSize).SizeValue()));
+  } else if (invoke->IsRecursive()) {
+    __ call(&frame_entry_label_);
   } else {
-    // temp = method;
-    LoadCurrentMethod(temp);
-    if (!invoke->IsRecursive()) {
-      // temp = temp->dex_cache_resolved_methods_;
-      __ movl(temp, Address(temp, ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
-      // temp = temp[index_in_cache]
-      __ movq(temp, Address(
-          temp, CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())));
-      // (temp + offset_of_quick_compiled_code)()
-      __ call(Address(temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-          kX86_64WordSize).SizeValue()));
-    } else {
-      __ call(&frame_entry_label_);
-    }
+    LocationSummary* locations = invoke->GetLocations();
+    CpuRegister reg = temp.AsRegister<CpuRegister>();
+    CpuRegister current_method =
+        locations->InAt(invoke->GetCurrentMethodInputIndex()).AsRegister<CpuRegister>();
+    // temp = temp->dex_cache_resolved_methods_;
+    __ movl(reg, Address(
+        current_method, ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
+    // temp = temp[index_in_cache]
+    __ movq(reg, Address(
+        reg, CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())));
+    // (temp + offset_of_quick_compiled_code)()
+    __ call(Address(reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+        kX86_64WordSize).SizeValue()));
   }
 
   DCHECK(!IsLeafMethod());
@@ -1334,6 +1336,10 @@
 
   IntrinsicLocationsBuilderX86_64 intrinsic(codegen_);
   if (intrinsic.TryDispatch(invoke)) {
+    LocationSummary* locations = invoke->GetLocations();
+    if (locations->CanCall()) {
+      locations->SetInAt(invoke->GetCurrentMethodInputIndex(), Location::RequiresRegister());
+    }
     return;
   }
 
@@ -1358,9 +1364,9 @@
     return;
   }
 
+  LocationSummary* locations = invoke->GetLocations();
   codegen_->GenerateStaticOrDirectCall(
-      invoke,
-      invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>());
+      invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
 
@@ -1390,12 +1396,8 @@
   Location receiver = locations->InAt(0);
   size_t class_offset = mirror::Object::ClassOffset().SizeValue();
   // temp = object->GetClass();
-  if (receiver.IsStackSlot()) {
-    __ movl(temp, Address(CpuRegister(RSP), receiver.GetStackIndex()));
-    __ movl(temp, Address(temp, class_offset));
-  } else {
-    __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
-  }
+  DCHECK(receiver.IsRegister());
+  __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
   codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetMethodAt(method_offset);
   __ movq(temp, Address(temp, method_offset));
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index c19e686..61f863c 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -277,7 +277,7 @@
     return false;
   }
 
-  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, CpuRegister temp);
+  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
 
   const X86_64InstructionSetFeatures& GetInstructionSetFeatures() const {
     return isa_features_;
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index b26afd1..ea613b2 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -70,6 +70,13 @@
             bool should_inline = callee_name.find("$inline$") != std::string::npos;
             CHECK(!should_inline) << "Could not inline " << callee_name;
           }
+        } else {
+          if (kIsDebugBuild) {
+            std::string callee_name =
+                PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
+            bool must_not_inline = callee_name.find("$noinline$") != std::string::npos;
+            CHECK(!must_not_inline) << "Should not have inlined " << callee_name;
+          }
         }
       }
       instruction = next;
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 5436ec2..749bedf 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -101,7 +101,8 @@
     MoveArguments(invoke_, codegen);
 
     if (invoke_->IsInvokeStaticOrDirect()) {
-      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
+      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
+                                          Location::RegisterLocation(kArtMethodRegister));
       RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
     } else {
       UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index d1dc5b3..c108ad5 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -110,7 +110,8 @@
     MoveArguments(invoke_, codegen);
 
     if (invoke_->IsInvokeStaticOrDirect()) {
-      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
+      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
+                                          LocationFrom(kArtMethodRegister));
       RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
     } else {
       UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 5bbbc72..424ac7c 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -138,7 +138,8 @@
     MoveArguments(invoke_, codegen);
 
     if (invoke_->IsInvokeStaticOrDirect()) {
-      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), EAX);
+      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
+                                          Location::RegisterLocation(EAX));
       RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
     } else {
       UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
@@ -732,7 +733,8 @@
   MoveArguments(invoke, codegen);
 
   DCHECK(invoke->IsInvokeStaticOrDirect());
-  codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(), EAX);
+  codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(),
+                                      Location::RegisterLocation(EAX));
   codegen->RecordPcInfo(invoke, invoke->GetDexPc());
 
   // Copy the result back to the expected output.
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index d6c90ff..8915314 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -129,7 +129,8 @@
     MoveArguments(invoke_, codegen);
 
     if (invoke_->IsInvokeStaticOrDirect()) {
-      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), CpuRegister(RDI));
+      codegen->GenerateStaticOrDirectCall(
+          invoke_->AsInvokeStaticOrDirect(), Location::RegisterLocation(RDI));
       RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
     } else {
       UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
@@ -609,7 +610,8 @@
   MoveArguments(invoke, codegen);
 
   DCHECK(invoke->IsInvokeStaticOrDirect());
-  codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(), CpuRegister(RDI));
+  codegen->GenerateStaticOrDirectCall(
+      invoke->AsInvokeStaticOrDirect(), Location::RegisterLocation(RDI));
   codegen->RecordPcInfo(invoke, invoke->GetDexPc());
 
   // Copy the result back to the expected output.
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index 09bbb33..66c5fb1 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -525,6 +525,8 @@
     return temps_.Size();
   }
 
+  bool HasTemps() const { return !temps_.IsEmpty(); }
+
   Location Out() const { return output_; }
 
   bool CanCall() const { return call_kind_ != kNoCall; }
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 4792734..d914363 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2528,7 +2528,9 @@
                         ClinitCheckRequirement clinit_check_requirement)
       : HInvoke(arena,
                 number_of_arguments,
-                clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u,
+                // There is one extra argument for the  HCurrentMethod node, and
+                // potentially one other if the clinit check is explicit.
+                clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 2u : 1u,
                 return_type,
                 dex_pc,
                 dex_method_index,
@@ -2550,6 +2552,7 @@
   bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); }
   bool IsStringInit() const { return string_init_offset_ != 0; }
   int32_t GetStringInitOffset() const { return string_init_offset_; }
+  uint32_t GetCurrentMethodInputIndex() const { return GetNumberOfArguments(); }
 
   // Is this instruction a call to a static method?
   bool IsStatic() const {
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index a381315..e38e49c 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -714,13 +714,15 @@
   if (defined_by != nullptr && !current->IsSplit()) {
     LocationSummary* locations = defined_by->GetLocations();
     if (!locations->OutputCanOverlapWithInputs() && locations->Out().IsUnallocated()) {
-      for (HInputIterator it(defined_by); !it.Done(); it.Advance()) {
+      for (size_t i = 0, e = defined_by->InputCount(); i < e; ++i) {
         // Take the last interval of the input. It is the location of that interval
         // that will be used at `defined_by`.
-        LiveInterval* interval = it.Current()->GetLiveInterval()->GetLastSibling();
+        LiveInterval* interval = defined_by->InputAt(i)->GetLiveInterval()->GetLastSibling();
         // Note that interval may have not been processed yet.
         // TODO: Handle non-split intervals last in the work list.
-        if (interval->HasRegister() && interval->SameRegisterKind(*current)) {
+        if (locations->InAt(i).IsValid()
+            && interval->HasRegister()
+            && interval->SameRegisterKind(*current)) {
           // The input must be live until the end of `defined_by`, to comply to
           // the linear scan algorithm. So we use `defined_by`'s end lifetime
           // position to check whether the input is dead or is inactive after
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index d5f977f..701dbb0 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -242,7 +242,7 @@
         HInstruction* input = current->InputAt(i);
         // Some instructions 'inline' their inputs, that is they do not need
         // to be materialized.
-        if (input->HasSsaIndex()) {
+        if (input->HasSsaIndex() && current->GetLocations()->InAt(i).IsValid()) {
           live_in->SetBit(input->GetSsaIndex());
           input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i);
         }
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 4667825..220ee6a 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -394,7 +394,7 @@
       first_range_->start_ = from;
     } else {
       // Instruction without uses.
-      DCHECK(!defined_by_->HasNonEnvironmentUses());
+      DCHECK(first_use_ == nullptr);
       DCHECK(from == defined_by_->GetLifetimePosition());
       first_range_ = last_range_ = range_search_start_ =
           new (allocator_) LiveRange(from, from + 2, nullptr);
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index d7efe1c..10ed0f4 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -104,11 +104,11 @@
             art::Thread::TopOfManagedStackOffset<__SIZEOF_POINTER__>().Int32Value())
 
 // Offset of field Thread::tlsPtr_.managed_stack.top_quick_frame_.
-#define THREAD_SELF_OFFSET (THREAD_CARD_TABLE_OFFSET + (8 * __SIZEOF_POINTER__))
+#define THREAD_SELF_OFFSET (THREAD_CARD_TABLE_OFFSET + (9 * __SIZEOF_POINTER__))
 ADD_TEST_EQ(THREAD_SELF_OFFSET,
             art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value())
 
-#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 146 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 147 * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
             art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value())
 #define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__)
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 431ef27..31140a8 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -277,9 +277,9 @@
       quick_to_interpreter_bridge_trampoline_(nullptr),
       image_pointer_size_(sizeof(void*)) {
   CHECK(intern_table_ != nullptr);
-  for (auto& root : find_array_class_cache_) {
-    root = GcRoot<mirror::Class>(nullptr);
-  }
+  static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_),
+                "Array cache size wrong.");
+  std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
 }
 
 void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path) {
@@ -5886,4 +5886,9 @@
   return method;
 }
 
+void ClassLinker::DropFindArrayClassCache() {
+  std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
+  find_array_class_cache_next_victim_ = 0;
+}
+
 }  // namespace art
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index fa8b2e7..d9935cb 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -480,6 +480,10 @@
 
   ArtMethod* CreateRuntimeMethod();
 
+  // Clear the ArrayClass cache. This is necessary when cleaning up for the image, as the cache
+  // entries are roots, but potentially not image classes.
+  void DropFindArrayClassCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
  private:
   const OatFile::OatMethod FindOatMethodFor(ArtMethod* method, bool* found)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 482f656..963dd02 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -88,7 +88,8 @@
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_end, managed_stack, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, managed_stack, suspend_trigger, sizeof(ManagedStack));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, suspend_trigger, jni_env, sizeof(void*));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jni_env, self, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jni_env, tmp_jni_env, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, tmp_jni_env, self, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, self, opeer, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, opeer, jpeer, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jpeer, stack_begin, sizeof(void*));
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 59d0259..20e791d 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2253,6 +2253,7 @@
   // to large objects.
   mod_union_table->SetCards();
   AddModUnionTable(mod_union_table);
+  large_object_space_->SetAllLargeObjectsAsZygoteObjects(self);
   if (collector::SemiSpace::kUseRememberedSet) {
     // Add a new remembered set for the post-zygote non-moving space.
     accounting::RememberedSet* post_zygote_non_moving_space_rem_set =
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index da4a930..52192e2 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -41,13 +41,13 @@
     // Keep valgrind happy if there is any large objects such as dex cache arrays which aren't
     // freed since they are held live by the class linker.
     MutexLock mu(Thread::Current(), lock_);
-    for (auto& m : mem_maps_) {
-      delete m.second;
+    for (auto& m : large_objects_) {
+      delete m.second.mem_map;
     }
   }
 
-  virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
-                                size_t* usable_size, size_t* bytes_tl_bulk_allocated)
+  mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
+                        size_t* usable_size, size_t* bytes_tl_bulk_allocated)
       OVERRIDE {
     mirror::Object* obj =
         LargeObjectMapSpace::Alloc(self, num_bytes + kValgrindRedZoneBytes * 2, bytes_allocated,
@@ -63,26 +63,35 @@
     return object_without_rdz;
   }
 
-  virtual size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE {
-    mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>(
-        reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
-    return LargeObjectMapSpace::AllocationSize(object_with_rdz, usable_size);
+  size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE {
+    return LargeObjectMapSpace::AllocationSize(ObjectWithRedzone(obj), usable_size);
   }
 
-  virtual size_t Free(Thread* self, mirror::Object* obj) OVERRIDE {
-    mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>(
-        reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
+  bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const OVERRIDE {
+    return LargeObjectMapSpace::IsZygoteLargeObject(self, ObjectWithRedzone(obj));
+  }
+
+  size_t Free(Thread* self, mirror::Object* obj) OVERRIDE {
+    mirror::Object* object_with_rdz = ObjectWithRedzone(obj);
     VALGRIND_MAKE_MEM_UNDEFINED(object_with_rdz, AllocationSize(obj, nullptr));
     return LargeObjectMapSpace::Free(self, object_with_rdz);
   }
 
   bool Contains(const mirror::Object* obj) const OVERRIDE {
-    mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>(
-        reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
-    return LargeObjectMapSpace::Contains(object_with_rdz);
+    return LargeObjectMapSpace::Contains(ObjectWithRedzone(obj));
   }
 
  private:
+  static const mirror::Object* ObjectWithRedzone(const mirror::Object* obj) {
+    return reinterpret_cast<const mirror::Object*>(
+        reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
+  }
+
+  static mirror::Object* ObjectWithRedzone(mirror::Object* obj) {
+    return reinterpret_cast<mirror::Object*>(
+        reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
+  }
+
   static constexpr size_t kValgrindRedZoneBytes = kPageSize;
 };
 
@@ -139,8 +148,7 @@
     CHECK(space_bitmap == nullptr) << obj_end << " overlaps with bitmap " << *space_bitmap;
   }
   MutexLock mu(self, lock_);
-  large_objects_.push_back(obj);
-  mem_maps_.Put(obj, mem_map);
+  large_objects_.Put(obj, LargeObject {mem_map, false /* not zygote */});
   const size_t allocation_size = mem_map->BaseSize();
   DCHECK(bytes_allocated != nullptr);
   begin_ = std::min(begin_, reinterpret_cast<uint8_t*>(obj));
@@ -161,28 +169,43 @@
   return obj;
 }
 
+bool LargeObjectMapSpace::IsZygoteLargeObject(Thread* self, mirror::Object* obj) const {
+  MutexLock mu(self, lock_);
+  auto it = large_objects_.find(obj);
+  CHECK(it != large_objects_.end());
+  return it->second.is_zygote;
+}
+
+void LargeObjectMapSpace::SetAllLargeObjectsAsZygoteObjects(Thread* self) {
+  MutexLock mu(self, lock_);
+  for (auto& pair : large_objects_) {
+    pair.second.is_zygote = true;
+  }
+}
+
 size_t LargeObjectMapSpace::Free(Thread* self, mirror::Object* ptr) {
   MutexLock mu(self, lock_);
-  MemMaps::iterator found = mem_maps_.find(ptr);
-  if (UNLIKELY(found == mem_maps_.end())) {
-    Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
+  auto it = large_objects_.find(ptr);
+  if (UNLIKELY(it == large_objects_.end())) {
+    Runtime::Current()->GetHeap()->DumpSpaces(LOG(INTERNAL_FATAL));
     LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live";
   }
-  const size_t map_size = found->second->BaseSize();
+  MemMap* mem_map = it->second.mem_map;
+  const size_t map_size = mem_map->BaseSize();
   DCHECK_GE(num_bytes_allocated_, map_size);
   size_t allocation_size = map_size;
   num_bytes_allocated_ -= allocation_size;
   --num_objects_allocated_;
-  delete found->second;
-  mem_maps_.erase(found);
+  delete mem_map;
+  large_objects_.erase(it);
   return allocation_size;
 }
 
 size_t LargeObjectMapSpace::AllocationSize(mirror::Object* obj, size_t* usable_size) {
   MutexLock mu(Thread::Current(), lock_);
-  auto found = mem_maps_.find(obj);
-  CHECK(found != mem_maps_.end()) << "Attempted to get size of a large object which is not live";
-  size_t alloc_size = found->second->BaseSize();
+  auto it = large_objects_.find(obj);
+  CHECK(it != large_objects_.end()) << "Attempted to get size of a large object which is not live";
+  size_t alloc_size = it->second.mem_map->BaseSize();
   if (usable_size != nullptr) {
     *usable_size = alloc_size;
   }
@@ -202,8 +225,8 @@
 
 void LargeObjectMapSpace::Walk(DlMallocSpace::WalkCallback callback, void* arg) {
   MutexLock mu(Thread::Current(), lock_);
-  for (auto it = mem_maps_.begin(); it != mem_maps_.end(); ++it) {
-    MemMap* mem_map = it->second;
+  for (auto& pair : large_objects_) {
+    MemMap* mem_map = pair.second.mem_map;
     callback(mem_map->Begin(), mem_map->End(), mem_map->Size(), arg);
     callback(nullptr, nullptr, 0, arg);
   }
@@ -213,22 +236,25 @@
   Thread* self = Thread::Current();
   if (lock_.IsExclusiveHeld(self)) {
     // We hold lock_ so do the check.
-    return mem_maps_.find(const_cast<mirror::Object*>(obj)) != mem_maps_.end();
+    return large_objects_.find(const_cast<mirror::Object*>(obj)) != large_objects_.end();
   } else {
     MutexLock mu(self, lock_);
-    return mem_maps_.find(const_cast<mirror::Object*>(obj)) != mem_maps_.end();
+    return large_objects_.find(const_cast<mirror::Object*>(obj)) != large_objects_.end();
   }
 }
 
 // Keeps track of allocation sizes + whether or not the previous allocation is free.
-// Used to coalesce free blocks and find the best fit block for an allocation.
+// Used to coalesce free blocks and find the best fit block for an allocation for best fit object
+// allocation. Each allocation has an AllocationInfo which contains the size of the previous free
+// block preceding it. Implemented in such a way that we can also find the iterator for any
+// allocation info pointer.
 class AllocationInfo {
  public:
   AllocationInfo() : prev_free_(0), alloc_size_(0) {
   }
   // Return the number of pages that the allocation info covers.
   size_t AlignSize() const {
-    return alloc_size_ & ~kFlagFree;
+    return alloc_size_ & kFlagsMask;
   }
   // Returns the allocation size in bytes.
   size_t ByteSize() const {
@@ -236,12 +262,23 @@
   }
   // Updates the allocation size and whether or not it is free.
   void SetByteSize(size_t size, bool free) {
+    DCHECK_EQ(size & ~kFlagsMask, 0u);
     DCHECK_ALIGNED(size, FreeListSpace::kAlignment);
-    alloc_size_ = (size / FreeListSpace::kAlignment) | (free ? kFlagFree : 0U);
+    alloc_size_ = (size / FreeListSpace::kAlignment) | (free ? kFlagFree : 0u);
   }
+  // Returns true if the block is free.
   bool IsFree() const {
     return (alloc_size_ & kFlagFree) != 0;
   }
+  // Return true if the large object is a zygote object.
+  bool IsZygoteObject() const {
+    return (alloc_size_ & kFlagZygote) != 0;
+  }
+  // Change the object to be a zygote object.
+  void SetZygoteObject() {
+    alloc_size_ |= kFlagZygote;
+  }
+  // Return true if this is a zygote large object.
   // Finds and returns the next non free allocation info after ourself.
   AllocationInfo* GetNextInfo() {
     return this + AlignSize();
@@ -275,10 +312,9 @@
   }
 
  private:
-  // Used to implement best fit object allocation. Each allocation has an AllocationInfo which
-  // contains the size of the previous free block preceding it. Implemented in such a way that we
-  // can also find the iterator for any allocation info pointer.
-  static constexpr uint32_t kFlagFree = 0x8000000;
+  static constexpr uint32_t kFlagFree = 0x80000000;  // If block is free.
+  static constexpr uint32_t kFlagZygote = 0x40000000;  // If the large object is a zygote object.
+  static constexpr uint32_t kFlagsMask = ~(kFlagFree | kFlagZygote);  // Combined flags for masking.
   // Contains the size of the previous free block with kAlignment as the unit. If 0 then the
   // allocation before us is not free.
   // These variables are undefined in the middle of allocations / free blocks.
@@ -493,7 +529,7 @@
 }
 
 void FreeListSpace::Dump(std::ostream& os) const {
-  MutexLock mu(Thread::Current(), const_cast<Mutex&>(lock_));
+  MutexLock mu(Thread::Current(), lock_);
   os << GetName() << " -"
      << " begin: " << reinterpret_cast<void*>(Begin())
      << " end: " << reinterpret_cast<void*>(End()) << "\n";
@@ -519,6 +555,24 @@
   }
 }
 
+bool FreeListSpace::IsZygoteLargeObject(Thread* self ATTRIBUTE_UNUSED, mirror::Object* obj) const {
+  const AllocationInfo* info = GetAllocationInfoForAddress(reinterpret_cast<uintptr_t>(obj));
+  DCHECK(info != nullptr);
+  return info->IsZygoteObject();
+}
+
+void FreeListSpace::SetAllLargeObjectsAsZygoteObjects(Thread* self) {
+  MutexLock mu(self, lock_);
+  uintptr_t free_end_start = reinterpret_cast<uintptr_t>(end_) - free_end_;
+  for (AllocationInfo* cur_info = GetAllocationInfoForAddress(reinterpret_cast<uintptr_t>(Begin())),
+      *end_info = GetAllocationInfoForAddress(free_end_start); cur_info < end_info;
+      cur_info = cur_info->GetNextInfo()) {
+    if (!cur_info->IsFree()) {
+      cur_info->SetZygoteObject();
+    }
+  }
+}
+
 void LargeObjectSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) {
   SweepCallbackContext* context = static_cast<SweepCallbackContext*>(arg);
   space::LargeObjectSpace* space = context->space->AsLargeObjectSpace();
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index d1f9386..45ed0cd 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -98,6 +98,12 @@
   void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Return true if the large object is a zygote large object. Potentially slow.
+  virtual bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const = 0;
+  // Called when we create the zygote space, mark all existing large objects as zygote large
+  // objects.
+  virtual void SetAllLargeObjectsAsZygoteObjects(Thread* self) = 0;
+
  protected:
   explicit LargeObjectSpace(const std::string& name, uint8_t* begin, uint8_t* end);
   static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg);
@@ -133,16 +139,20 @@
   bool Contains(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS;
 
  protected:
+  struct LargeObject {
+    MemMap* mem_map;
+    bool is_zygote;
+  };
   explicit LargeObjectMapSpace(const std::string& name);
   virtual ~LargeObjectMapSpace() {}
 
+  bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const OVERRIDE LOCKS_EXCLUDED(lock_);
+  void SetAllLargeObjectsAsZygoteObjects(Thread* self) OVERRIDE LOCKS_EXCLUDED(lock_);
+
   // Used to ensure mutual exclusion when the allocation spaces data structures are being modified.
   mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  std::vector<mirror::Object*, TrackingAllocator<mirror::Object*, kAllocatorTagLOS>> large_objects_
+  AllocationTrackingSafeMap<mirror::Object*, LargeObject, kAllocatorTagLOSMaps> large_objects_
       GUARDED_BY(lock_);
-  typedef SafeMap<mirror::Object*, MemMap*, std::less<mirror::Object*>,
-      TrackingAllocator<std::pair<mirror::Object*, MemMap*>, kAllocatorTagLOSMaps>> MemMaps;
-  MemMaps mem_maps_ GUARDED_BY(lock_);
 };
 
 // A continuous large object space with a free-list to handle holes.
@@ -177,6 +187,8 @@
   }
   // Removes header from the free blocks set by finding the corresponding iterator and erasing it.
   void RemoveFreePrev(AllocationInfo* info) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+  bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const OVERRIDE;
+  void SetAllLargeObjectsAsZygoteObjects(Thread* self) OVERRIDE;
 
   class SortByPrevFree {
    public:
@@ -192,7 +204,7 @@
   std::unique_ptr<MemMap> allocation_info_map_;
   AllocationInfo* allocation_info_;
 
-  Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   // Free bytes at the end of the space.
   size_t free_end_ GUARDED_BY(lock_);
   FreeBlocks free_blocks_ GUARDED_BY(lock_);
diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc
index f04a7ca..05b484a 100644
--- a/runtime/gc/space/large_object_space_test.cc
+++ b/runtime/gc/space/large_object_space_test.cc
@@ -34,6 +34,7 @@
 
 void LargeObjectSpaceTest::LargeObjectTest() {
   size_t rand_seed = 0;
+  Thread* const self = Thread::Current();
   for (size_t i = 0; i < 2; ++i) {
     LargeObjectSpace* los = nullptr;
     if (i == 0) {
@@ -51,8 +52,8 @@
         size_t request_size = test_rand(&rand_seed) % max_allocation_size;
         size_t allocation_size = 0;
         size_t bytes_tl_bulk_allocated;
-        mirror::Object* obj = los->Alloc(Thread::Current(), request_size, &allocation_size,
-                                         nullptr, &bytes_tl_bulk_allocated);
+        mirror::Object* obj = los->Alloc(self, request_size, &allocation_size, nullptr,
+                                         &bytes_tl_bulk_allocated);
         ASSERT_TRUE(obj != nullptr);
         ASSERT_EQ(allocation_size, los->AllocationSize(obj, nullptr));
         ASSERT_GE(allocation_size, request_size);
@@ -70,8 +71,21 @@
         }
       }
 
+      // Check the zygote flag for the first phase.
+      if (phase == 0) {
+        for (const auto& pair : requests) {
+          mirror::Object* obj = pair.first;
+          ASSERT_FALSE(los->IsZygoteLargeObject(self, obj));
+        }
+        los->SetAllLargeObjectsAsZygoteObjects(self);
+        for (const auto& pair : requests) {
+          mirror::Object* obj = pair.first;
+          ASSERT_TRUE(los->IsZygoteLargeObject(self, obj));
+        }
+      }
+
       // Free 1 / 2 the allocations the first phase, and all the second phase.
-      size_t limit = !phase ? requests.size() / 2 : 0;
+      size_t limit = phase == 0 ? requests.size() / 2 : 0;
       while (requests.size() > limit) {
         mirror::Object* obj = requests.back().first;
         size_t request_size = requests.back().second;
@@ -88,7 +102,7 @@
 
     size_t bytes_allocated = 0, bytes_tl_bulk_allocated;
     // Checks that the coalescing works.
-    mirror::Object* obj = los->Alloc(Thread::Current(), 100 * MB, &bytes_allocated, nullptr,
+    mirror::Object* obj = los->Alloc(self, 100 * MB, &bytes_allocated, nullptr,
                                      &bytes_tl_bulk_allocated);
     EXPECT_TRUE(obj != nullptr);
     los->Free(Thread::Current(), obj);
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 917fe43..6e0e56e 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -891,8 +891,8 @@
     return;
   }
 
-  gc::space::ContinuousSpace* space =
-      Runtime::Current()->GetHeap()->FindContinuousSpaceFromObject(obj, true);
+  gc::Heap* const heap = Runtime::Current()->GetHeap();
+  const gc::space::ContinuousSpace* const space = heap->FindContinuousSpaceFromObject(obj, true);
   HprofHeapId heap_type = HPROF_HEAP_APP;
   if (space != nullptr) {
     if (space->IsZygoteSpace()) {
@@ -900,6 +900,11 @@
     } else if (space->IsImageSpace()) {
       heap_type = HPROF_HEAP_IMAGE;
     }
+  } else {
+    const auto* los = heap->GetLargeObjectsSpace();
+    if (los->Contains(obj) && los->IsZygoteLargeObject(Thread::Current(), obj)) {
+      heap_type = HPROF_HEAP_ZYGOTE;
+    }
   }
   CheckHeapSegmentConstraints();
 
@@ -1040,7 +1045,7 @@
   }
 
   // Instance fields for this class (no superclass fields)
-  int iFieldCount = klass->IsObjectClass() ? 0 : klass->NumInstanceFields();
+  int iFieldCount = klass->NumInstanceFields();
   if (klass->IsStringClass()) {
     __ AddU2((uint16_t)iFieldCount + 1);
   } else {
@@ -1114,7 +1119,7 @@
   // Write the instance data;  fields for this class, followed by super class fields,
   // and so on. Don't write the klass or monitor fields of Object.class.
   mirror::Class* orig_klass = klass;
-  while (!klass->IsObjectClass()) {
+  do {
     int ifieldCount = klass->NumInstanceFields();
     for (int i = 0; i < ifieldCount; ++i) {
       ArtField* f = klass->GetInstanceField(i);
@@ -1146,7 +1151,7 @@
     }
 
     klass = klass->GetSuperClass();
-  }
+  } while (klass != nullptr);
 
   // Output native value character array for strings.
   if (orig_klass->IsStringClass()) {
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index cf4233c..d8c1ec1 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -221,7 +221,7 @@
   // We call this here so that we can try and generate a full error
   // message with the overlapping mapping. There's no guarantee that
   // that there will be an overlap though, since
-  // - The kernel is not *required* to honour expected_ptr unless MAP_FIXED is
+  // - The kernel is not *required* to honor expected_ptr unless MAP_FIXED is
   //   true, even if there is no overlap
   // - There might have been an overlap at the point of mmap, but the
   //   overlapping region has since been unmapped.
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 0c39f2b..1515630 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -55,60 +55,29 @@
   return nullptr;
 }
 
-static jint VMClassLoader_getBootClassPathSize(JNIEnv*, jclass) {
-  return Runtime::Current()->GetClassLinker()->GetBootClassPath().size();
-}
-
 /*
- * Returns a string URL for a resource with the specified 'javaName' in
- * entry 'index' of the boot class path.
- *
- * We return a newly-allocated String in the following form:
- *
- *   jar:file://path!/name
- *
- * Where "path" is the bootstrap class path entry and "name" is the string
- * passed into this method.  "path" needs to be an absolute path (starting
- * with '/'); if it's not we'd need to make it absolute as part of forming
- * the URL string.
+ * Returns an array of entries from the boot classpath that could contain resources.
  */
-static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstring javaName,
-                                                      jint index) {
-  ScopedUtfChars name(env, javaName);
-  if (name.c_str() == nullptr) {
-    return nullptr;
-  }
-
+static jobjectArray VMClassLoader_getBootClassPathEntries(JNIEnv* env, jclass) {
   const std::vector<const DexFile*>& path =
       Runtime::Current()->GetClassLinker()->GetBootClassPath();
-  if (index < 0 || size_t(index) >= path.size()) {
-    return nullptr;
-  }
-  const DexFile* dex_file = path[index];
+  jclass stringClass = env->FindClass("java/lang/String");
+  jobjectArray array = env->NewObjectArray(path.size(), stringClass, nullptr);
+  for (size_t i = 0; i < path.size(); ++i) {
+    const DexFile* dex_file = path[i];
 
-  // For multidex locations, e.g., x.jar:classes2.dex, we want to look into x.jar.
-  const std::string& location(dex_file->GetBaseLocation());
+    // For multidex locations, e.g., x.jar:classes2.dex, we want to look into x.jar.
+    const std::string& location(dex_file->GetBaseLocation());
 
-  std::string error_msg;
-  std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(location.c_str(), &error_msg));
-  if (zip_archive.get() == nullptr) {
-    LOG(WARNING) << "Failed to open zip archive '" << location << "': " << error_msg;
-    return nullptr;
+    jstring javaPath = env->NewStringUTF(location.c_str());
+    env->SetObjectArrayElement(array, i, javaPath);
   }
-  std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(name.c_str(), &error_msg));
-  if (zip_entry.get() == nullptr) {
-    return nullptr;
-  }
-
-  std::string url;
-  StringAppendF(&url, "jar:file://%s!/%s", location.c_str(), name.c_str());
-  return env->NewStringUTF(url.c_str());
+  return array;
 }
 
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMClassLoader, findLoadedClass, "!(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;"),
-  NATIVE_METHOD(VMClassLoader, getBootClassPathResource, "(Ljava/lang/String;I)Ljava/lang/String;"),
-  NATIVE_METHOD(VMClassLoader, getBootClassPathSize, "!()I"),
+  NATIVE_METHOD(VMClassLoader, getBootClassPathEntries, "()[Ljava/lang/String;"),
 };
 
 void register_java_lang_VMClassLoader(JNIEnv* env) {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 65999f7..67f611e 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -196,7 +196,12 @@
     // Check that if we got here we cannot be shutting down (as shutdown should never have started
     // while threads are being born).
     CHECK(!runtime->IsShuttingDownLocked());
-    CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM()));
+    // Note: given that the JNIEnv is created in the parent thread, the only failure point here is
+    //       a mess in InitStackHwm. We do not have a reasonable way to recover from that, so abort
+    //       the runtime in such a case. In case this ever changes, we need to make sure here to
+    //       delete the tmp_jni_env, as we own it at this point.
+    CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM(), self->tlsPtr_.tmp_jni_env));
+    self->tlsPtr_.tmp_jni_env = nullptr;
     Runtime::Current()->EndThreadBirth();
   }
   {
@@ -358,37 +363,59 @@
   env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
                     reinterpret_cast<jlong>(child_thread));
 
-  pthread_t new_pthread;
-  pthread_attr_t attr;
-  CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread");
-  CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED), "PTHREAD_CREATE_DETACHED");
-  CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);
-  int pthread_create_result = pthread_create(&new_pthread, &attr, Thread::CreateCallback, child_thread);
-  CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");
+  // Try to allocate a JNIEnvExt for the thread. We do this here as we might be out of memory and
+  // do not have a good way to report this on the child's side.
+  std::unique_ptr<JNIEnvExt> child_jni_env_ext(
+      JNIEnvExt::Create(child_thread, Runtime::Current()->GetJavaVM()));
 
-  if (pthread_create_result != 0) {
-    // pthread_create(3) failed, so clean up.
-    {
-      MutexLock mu(self, *Locks::runtime_shutdown_lock_);
-      runtime->EndThreadBirth();
+  int pthread_create_result = 0;
+  if (child_jni_env_ext.get() != nullptr) {
+    pthread_t new_pthread;
+    pthread_attr_t attr;
+    child_thread->tlsPtr_.tmp_jni_env = child_jni_env_ext.get();
+    CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread");
+    CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED),
+                       "PTHREAD_CREATE_DETACHED");
+    CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);
+    pthread_create_result = pthread_create(&new_pthread,
+                                           &attr,
+                                           Thread::CreateCallback,
+                                           child_thread);
+    CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");
+
+    if (pthread_create_result == 0) {
+      // pthread_create started the new thread. The child is now responsible for managing the
+      // JNIEnvExt we created.
+      // Note: we can't check for tmp_jni_env == nullptr, as that would require synchronization
+      //       between the threads.
+      child_jni_env_ext.release();
+      return;
     }
-    // Manually delete the global reference since Thread::Init will not have been run.
-    env->DeleteGlobalRef(child_thread->tlsPtr_.jpeer);
-    child_thread->tlsPtr_.jpeer = nullptr;
-    delete child_thread;
-    child_thread = nullptr;
-    // TODO: remove from thread group?
-    env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);
-    {
-      std::string msg(StringPrintf("pthread_create (%s stack) failed: %s",
-                                   PrettySize(stack_size).c_str(), strerror(pthread_create_result)));
-      ScopedObjectAccess soa(env);
-      soa.Self()->ThrowOutOfMemoryError(msg.c_str());
-    }
+  }
+
+  // Either JNIEnvExt::Create or pthread_create(3) failed, so clean up.
+  {
+    MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+    runtime->EndThreadBirth();
+  }
+  // Manually delete the global reference since Thread::Init will not have been run.
+  env->DeleteGlobalRef(child_thread->tlsPtr_.jpeer);
+  child_thread->tlsPtr_.jpeer = nullptr;
+  delete child_thread;
+  child_thread = nullptr;
+  // TODO: remove from thread group?
+  env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);
+  {
+    std::string msg(child_jni_env_ext.get() == nullptr ?
+        "Could not allocate JNI Env" :
+        StringPrintf("pthread_create (%s stack) failed: %s",
+                                 PrettySize(stack_size).c_str(), strerror(pthread_create_result)));
+    ScopedObjectAccess soa(env);
+    soa.Self()->ThrowOutOfMemoryError(msg.c_str());
   }
 }
 
-bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) {
+bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) {
   // This function does all the initialization that must be run by the native thread it applies to.
   // (When we create a new thread from managed code, we allocate the Thread* in Thread::Create so
   // we can handshake with the corresponding native thread when it's ready.) Check this native
@@ -415,9 +442,15 @@
 
   tls32_.thin_lock_thread_id = thread_list->AllocThreadId(this);
 
-  tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm);
-  if (tlsPtr_.jni_env == nullptr) {
-    return false;
+  if (jni_env_ext != nullptr) {
+    DCHECK_EQ(jni_env_ext->vm, java_vm);
+    DCHECK_EQ(jni_env_ext->self, this);
+    tlsPtr_.jni_env = jni_env_ext;
+  } else {
+    tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm);
+    if (tlsPtr_.jni_env == nullptr) {
+      return false;
+    }
   }
 
   thread_list->Register(this);
diff --git a/runtime/thread.h b/runtime/thread.h
index 8c2e215..3f0d0a5 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -975,7 +975,15 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void RemoveFromThreadGroup(ScopedObjectAccess& soa) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool Init(ThreadList*, JavaVMExt*) EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_);
+  // Initialize a thread.
+  //
+  // The third parameter is not mandatory. If given, the thread will use this JNIEnvExt. In case
+  // Init succeeds, this means the thread takes ownership of it. If Init fails, it is the caller's
+  // responsibility to destroy the given JNIEnvExt. If the parameter is null, Init will try to
+  // create a JNIEnvExt on its own (and potentially fail at that stage, indicated by a return value
+  // of false).
+  bool Init(ThreadList*, JavaVMExt*, JNIEnvExt* jni_env_ext = nullptr)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_);
   void InitCardTable();
   void InitCpu();
   void CleanupCpu();
@@ -1111,8 +1119,8 @@
 
   struct PACKED(4) tls_ptr_sized_values {
       tls_ptr_sized_values() : card_table(nullptr), exception(nullptr), stack_end(nullptr),
-      managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), self(nullptr), opeer(nullptr),
-      jpeer(nullptr), stack_begin(nullptr), stack_size(0),
+      managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), tmp_jni_env(nullptr),
+      self(nullptr), opeer(nullptr), jpeer(nullptr), stack_begin(nullptr), stack_size(0),
       stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr),
       top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr),
       instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
@@ -1144,6 +1152,10 @@
     // Every thread may have an associated JNI environment
     JNIEnvExt* jni_env;
 
+    // Temporary storage to transfer a pre-allocated JNIEnvExt from the creating thread to the
+    // created thread.
+    JNIEnvExt* tmp_jni_env;
+
     // Initialized to "this". On certain architectures (such as x86) reading off of Thread::Current
     // is easy but getting the address of Thread::Current is hard. This field can be read off of
     // Thread::Current to give the address.
diff --git a/test/004-StackWalk/src/Main.java b/test/004-StackWalk/src/Main.java
index 782f51d..9a1d0ab 100644
--- a/test/004-StackWalk/src/Main.java
+++ b/test/004-StackWalk/src/Main.java
@@ -2,7 +2,7 @@
   public Main() {
   }
 
-  int f() throws Exception {
+  int $noinline$f() throws Exception {
     g(1);
     g(2);
 
@@ -93,6 +93,6 @@
 
   public static void main(String[] args) throws Exception {
     Main st = new Main();
-    st.f();
+    st.$noinline$f();
   }
 }
diff --git a/test/067-preemptive-unpark/src/Main.java b/test/067-preemptive-unpark/src/Main.java
index 2c099b9..beb3262 100644
--- a/test/067-preemptive-unpark/src/Main.java
+++ b/test/067-preemptive-unpark/src/Main.java
@@ -40,22 +40,24 @@
     /**
      * Set up {@link #UNSAFE}.
      */
-    public static void setUp() {
+    public static void setUp() throws Exception{
         /*
          * Subvert the access check to get the unique Unsafe instance.
          * We can do this because there's no security manager
          * installed when running the test.
          */
+        Field field = null;
         try {
-            Field field = Unsafe.class.getDeclaredField("THE_ONE");
-            field.setAccessible(true);
-
-            UNSAFE = (Unsafe) field.get(null);
-        } catch (NoSuchFieldException ex) {
-            throw new RuntimeException(ex);
-        } catch (IllegalAccessException ex) {
-            throw new RuntimeException(ex);
+            field = Unsafe.class.getDeclaredField("THE_ONE");
+        } catch (NoSuchFieldException e1) {
+            try {
+                field = Unsafe.class.getDeclaredField("theUnsafe");
+            } catch (NoSuchFieldException e2) {
+                throw new RuntimeException("Failed to find THE_ONE or theUnsafe");
+            }
         }
+        field.setAccessible(true);
+        UNSAFE = (Unsafe) field.get(null);
     }
 
     /**
diff --git a/test/133-static-invoke-super/src/Main.java b/test/133-static-invoke-super/src/Main.java
index 7cfd099..e694998 100644
--- a/test/133-static-invoke-super/src/Main.java
+++ b/test/133-static-invoke-super/src/Main.java
@@ -26,14 +26,14 @@
         run(timing);
     }
 
-    static int testBasis(int interations) {
-      (new SubClass()).testDirect(interations);
-      return interations;
+    static int testBasis(int iterations) {
+      (new SubClass()).testDirect(iterations);
+      return iterations;
     }
 
-    static int testStatic(int interations) {
-      (new SubClass()).testStatic(interations);
-      return interations;
+    static int testStatic(int iterations) {
+      (new SubClass()).testStatic(iterations);
+      return iterations;
     }
 
     static public void run(boolean timing) {
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
index 447b9b8..9f8f417 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -138,554 +138,554 @@
   }
 
   private static void byteToLong() {
-    assertLongEquals(1L, $opt$ByteToLong((byte)1));
-    assertLongEquals(0L, $opt$ByteToLong((byte)0));
-    assertLongEquals(-1L, $opt$ByteToLong((byte)-1));
-    assertLongEquals(51L, $opt$ByteToLong((byte)51));
-    assertLongEquals(-51L, $opt$ByteToLong((byte)-51));
-    assertLongEquals(127L, $opt$ByteToLong((byte)127));  // 2^7 - 1
-    assertLongEquals(-127L, $opt$ByteToLong((byte)-127));  // -(2^7 - 1)
-    assertLongEquals(-128L, $opt$ByteToLong((byte)-128));  // -(2^7)
+    assertLongEquals(1L, $opt$noinline$ByteToLong((byte)1));
+    assertLongEquals(0L, $opt$noinline$ByteToLong((byte)0));
+    assertLongEquals(-1L, $opt$noinline$ByteToLong((byte)-1));
+    assertLongEquals(51L, $opt$noinline$ByteToLong((byte)51));
+    assertLongEquals(-51L, $opt$noinline$ByteToLong((byte)-51));
+    assertLongEquals(127L, $opt$noinline$ByteToLong((byte)127));  // 2^7 - 1
+    assertLongEquals(-127L, $opt$noinline$ByteToLong((byte)-127));  // -(2^7 - 1)
+    assertLongEquals(-128L, $opt$noinline$ByteToLong((byte)-128));  // -(2^7)
   }
 
   private static void shortToLong() {
-    assertLongEquals(1L, $opt$ShortToLong((short)1));
-    assertLongEquals(0L, $opt$ShortToLong((short)0));
-    assertLongEquals(-1L, $opt$ShortToLong((short)-1));
-    assertLongEquals(51L, $opt$ShortToLong((short)51));
-    assertLongEquals(-51L, $opt$ShortToLong((short)-51));
-    assertLongEquals(32767L, $opt$ShortToLong((short)32767));  // 2^15 - 1
-    assertLongEquals(-32767L, $opt$ShortToLong((short)-32767));  // -(2^15 - 1)
-    assertLongEquals(-32768L, $opt$ShortToLong((short)-32768));  // -(2^15)
+    assertLongEquals(1L, $opt$noinline$ShortToLong((short)1));
+    assertLongEquals(0L, $opt$noinline$ShortToLong((short)0));
+    assertLongEquals(-1L, $opt$noinline$ShortToLong((short)-1));
+    assertLongEquals(51L, $opt$noinline$ShortToLong((short)51));
+    assertLongEquals(-51L, $opt$noinline$ShortToLong((short)-51));
+    assertLongEquals(32767L, $opt$noinline$ShortToLong((short)32767));  // 2^15 - 1
+    assertLongEquals(-32767L, $opt$noinline$ShortToLong((short)-32767));  // -(2^15 - 1)
+    assertLongEquals(-32768L, $opt$noinline$ShortToLong((short)-32768));  // -(2^15)
   }
 
   private static void intToLong() {
-    assertLongEquals(1L, $opt$IntToLong(1));
-    assertLongEquals(0L, $opt$IntToLong(0));
-    assertLongEquals(-1L, $opt$IntToLong(-1));
-    assertLongEquals(51L, $opt$IntToLong(51));
-    assertLongEquals(-51L, $opt$IntToLong(-51));
-    assertLongEquals(2147483647L, $opt$IntToLong(2147483647));  // 2^31 - 1
-    assertLongEquals(-2147483647L, $opt$IntToLong(-2147483647));  // -(2^31 - 1)
-    assertLongEquals(-2147483648L, $opt$IntToLong(-2147483648));  // -(2^31)
+    assertLongEquals(1L, $opt$noinline$IntToLong(1));
+    assertLongEquals(0L, $opt$noinline$IntToLong(0));
+    assertLongEquals(-1L, $opt$noinline$IntToLong(-1));
+    assertLongEquals(51L, $opt$noinline$IntToLong(51));
+    assertLongEquals(-51L, $opt$noinline$IntToLong(-51));
+    assertLongEquals(2147483647L, $opt$noinline$IntToLong(2147483647));  // 2^31 - 1
+    assertLongEquals(-2147483647L, $opt$noinline$IntToLong(-2147483647));  // -(2^31 - 1)
+    assertLongEquals(-2147483648L, $opt$noinline$IntToLong(-2147483648));  // -(2^31)
   }
 
   private static void charToLong() {
-    assertLongEquals(1L, $opt$CharToLong((char)1));
-    assertLongEquals(0L, $opt$CharToLong((char)0));
-    assertLongEquals(51L, $opt$CharToLong((char)51));
-    assertLongEquals(32767L, $opt$CharToLong((char)32767));  // 2^15 - 1
-    assertLongEquals(65535L, $opt$CharToLong((char)65535));  // 2^16 - 1
-    assertLongEquals(65535L, $opt$CharToLong((char)-1));
-    assertLongEquals(65485L, $opt$CharToLong((char)-51));
-    assertLongEquals(32769L, $opt$CharToLong((char)-32767));  // -(2^15 - 1)
-    assertLongEquals(32768L, $opt$CharToLong((char)-32768));  // -(2^15)
+    assertLongEquals(1L, $opt$noinline$CharToLong((char)1));
+    assertLongEquals(0L, $opt$noinline$CharToLong((char)0));
+    assertLongEquals(51L, $opt$noinline$CharToLong((char)51));
+    assertLongEquals(32767L, $opt$noinline$CharToLong((char)32767));  // 2^15 - 1
+    assertLongEquals(65535L, $opt$noinline$CharToLong((char)65535));  // 2^16 - 1
+    assertLongEquals(65535L, $opt$noinline$CharToLong((char)-1));
+    assertLongEquals(65485L, $opt$noinline$CharToLong((char)-51));
+    assertLongEquals(32769L, $opt$noinline$CharToLong((char)-32767));  // -(2^15 - 1)
+    assertLongEquals(32768L, $opt$noinline$CharToLong((char)-32768));  // -(2^15)
   }
 
   private static void byteToFloat() {
-    assertFloatEquals(1F, $opt$ByteToFloat((byte)1));
-    assertFloatEquals(0F, $opt$ByteToFloat((byte)0));
-    assertFloatEquals(-1F, $opt$ByteToFloat((byte)-1));
-    assertFloatEquals(51F, $opt$ByteToFloat((byte)51));
-    assertFloatEquals(-51F, $opt$ByteToFloat((byte)-51));
-    assertFloatEquals(127F, $opt$ByteToFloat((byte)127));  // 2^7 - 1
-    assertFloatEquals(-127F, $opt$ByteToFloat((byte)-127));  // -(2^7 - 1)
-    assertFloatEquals(-128F, $opt$ByteToFloat((byte)-128));  // -(2^7)
+    assertFloatEquals(1F, $opt$noinline$ByteToFloat((byte)1));
+    assertFloatEquals(0F, $opt$noinline$ByteToFloat((byte)0));
+    assertFloatEquals(-1F, $opt$noinline$ByteToFloat((byte)-1));
+    assertFloatEquals(51F, $opt$noinline$ByteToFloat((byte)51));
+    assertFloatEquals(-51F, $opt$noinline$ByteToFloat((byte)-51));
+    assertFloatEquals(127F, $opt$noinline$ByteToFloat((byte)127));  // 2^7 - 1
+    assertFloatEquals(-127F, $opt$noinline$ByteToFloat((byte)-127));  // -(2^7 - 1)
+    assertFloatEquals(-128F, $opt$noinline$ByteToFloat((byte)-128));  // -(2^7)
   }
 
   private static void shortToFloat() {
-    assertFloatEquals(1F, $opt$ShortToFloat((short)1));
-    assertFloatEquals(0F, $opt$ShortToFloat((short)0));
-    assertFloatEquals(-1F, $opt$ShortToFloat((short)-1));
-    assertFloatEquals(51F, $opt$ShortToFloat((short)51));
-    assertFloatEquals(-51F, $opt$ShortToFloat((short)-51));
-    assertFloatEquals(32767F, $opt$ShortToFloat((short)32767));  // 2^15 - 1
-    assertFloatEquals(-32767F, $opt$ShortToFloat((short)-32767));  // -(2^15 - 1)
-    assertFloatEquals(-32768F, $opt$ShortToFloat((short)-32768));  // -(2^15)
+    assertFloatEquals(1F, $opt$noinline$ShortToFloat((short)1));
+    assertFloatEquals(0F, $opt$noinline$ShortToFloat((short)0));
+    assertFloatEquals(-1F, $opt$noinline$ShortToFloat((short)-1));
+    assertFloatEquals(51F, $opt$noinline$ShortToFloat((short)51));
+    assertFloatEquals(-51F, $opt$noinline$ShortToFloat((short)-51));
+    assertFloatEquals(32767F, $opt$noinline$ShortToFloat((short)32767));  // 2^15 - 1
+    assertFloatEquals(-32767F, $opt$noinline$ShortToFloat((short)-32767));  // -(2^15 - 1)
+    assertFloatEquals(-32768F, $opt$noinline$ShortToFloat((short)-32768));  // -(2^15)
   }
 
   private static void intToFloat() {
-    assertFloatEquals(1F, $opt$IntToFloat(1));
-    assertFloatEquals(0F, $opt$IntToFloat(0));
-    assertFloatEquals(-1F, $opt$IntToFloat(-1));
-    assertFloatEquals(51F, $opt$IntToFloat(51));
-    assertFloatEquals(-51F, $opt$IntToFloat(-51));
-    assertFloatEquals(16777215F, $opt$IntToFloat(16777215));  // 2^24 - 1
-    assertFloatEquals(-16777215F, $opt$IntToFloat(-16777215));  // -(2^24 - 1)
-    assertFloatEquals(16777216F, $opt$IntToFloat(16777216));  // 2^24
-    assertFloatEquals(-16777216F, $opt$IntToFloat(-16777216));  // -(2^24)
-    assertFloatEquals(2147483647F, $opt$IntToFloat(2147483647));  // 2^31 - 1
-    assertFloatEquals(-2147483648F, $opt$IntToFloat(-2147483648));  // -(2^31)
+    assertFloatEquals(1F, $opt$noinline$IntToFloat(1));
+    assertFloatEquals(0F, $opt$noinline$IntToFloat(0));
+    assertFloatEquals(-1F, $opt$noinline$IntToFloat(-1));
+    assertFloatEquals(51F, $opt$noinline$IntToFloat(51));
+    assertFloatEquals(-51F, $opt$noinline$IntToFloat(-51));
+    assertFloatEquals(16777215F, $opt$noinline$IntToFloat(16777215));  // 2^24 - 1
+    assertFloatEquals(-16777215F, $opt$noinline$IntToFloat(-16777215));  // -(2^24 - 1)
+    assertFloatEquals(16777216F, $opt$noinline$IntToFloat(16777216));  // 2^24
+    assertFloatEquals(-16777216F, $opt$noinline$IntToFloat(-16777216));  // -(2^24)
+    assertFloatEquals(2147483647F, $opt$noinline$IntToFloat(2147483647));  // 2^31 - 1
+    assertFloatEquals(-2147483648F, $opt$noinline$IntToFloat(-2147483648));  // -(2^31)
   }
 
   private static void charToFloat() {
-    assertFloatEquals(1F, $opt$CharToFloat((char)1));
-    assertFloatEquals(0F, $opt$CharToFloat((char)0));
-    assertFloatEquals(51F, $opt$CharToFloat((char)51));
-    assertFloatEquals(32767F, $opt$CharToFloat((char)32767));  // 2^15 - 1
-    assertFloatEquals(65535F, $opt$CharToFloat((char)65535));  // 2^16 - 1
-    assertFloatEquals(65535F, $opt$CharToFloat((char)-1));
-    assertFloatEquals(65485F, $opt$CharToFloat((char)-51));
-    assertFloatEquals(32769F, $opt$CharToFloat((char)-32767));  // -(2^15 - 1)
-    assertFloatEquals(32768F, $opt$CharToFloat((char)-32768));  // -(2^15)
+    assertFloatEquals(1F, $opt$noinline$CharToFloat((char)1));
+    assertFloatEquals(0F, $opt$noinline$CharToFloat((char)0));
+    assertFloatEquals(51F, $opt$noinline$CharToFloat((char)51));
+    assertFloatEquals(32767F, $opt$noinline$CharToFloat((char)32767));  // 2^15 - 1
+    assertFloatEquals(65535F, $opt$noinline$CharToFloat((char)65535));  // 2^16 - 1
+    assertFloatEquals(65535F, $opt$noinline$CharToFloat((char)-1));
+    assertFloatEquals(65485F, $opt$noinline$CharToFloat((char)-51));
+    assertFloatEquals(32769F, $opt$noinline$CharToFloat((char)-32767));  // -(2^15 - 1)
+    assertFloatEquals(32768F, $opt$noinline$CharToFloat((char)-32768));  // -(2^15)
   }
 
   private static void byteToDouble() {
-    assertDoubleEquals(1D, $opt$ByteToDouble((byte)1));
-    assertDoubleEquals(0D, $opt$ByteToDouble((byte)0));
-    assertDoubleEquals(-1D, $opt$ByteToDouble((byte)-1));
-    assertDoubleEquals(51D, $opt$ByteToDouble((byte)51));
-    assertDoubleEquals(-51D, $opt$ByteToDouble((byte)-51));
-    assertDoubleEquals(127D, $opt$ByteToDouble((byte)127));  // 2^7 - 1
-    assertDoubleEquals(-127D, $opt$ByteToDouble((byte)-127));  // -(2^7 - 1)
-    assertDoubleEquals(-128D, $opt$ByteToDouble((byte)-128));  // -(2^7)
+    assertDoubleEquals(1D, $opt$noinline$ByteToDouble((byte)1));
+    assertDoubleEquals(0D, $opt$noinline$ByteToDouble((byte)0));
+    assertDoubleEquals(-1D, $opt$noinline$ByteToDouble((byte)-1));
+    assertDoubleEquals(51D, $opt$noinline$ByteToDouble((byte)51));
+    assertDoubleEquals(-51D, $opt$noinline$ByteToDouble((byte)-51));
+    assertDoubleEquals(127D, $opt$noinline$ByteToDouble((byte)127));  // 2^7 - 1
+    assertDoubleEquals(-127D, $opt$noinline$ByteToDouble((byte)-127));  // -(2^7 - 1)
+    assertDoubleEquals(-128D, $opt$noinline$ByteToDouble((byte)-128));  // -(2^7)
   }
 
   private static void shortToDouble() {
-    assertDoubleEquals(1D, $opt$ShortToDouble((short)1));
-    assertDoubleEquals(0D, $opt$ShortToDouble((short)0));
-    assertDoubleEquals(-1D, $opt$ShortToDouble((short)-1));
-    assertDoubleEquals(51D, $opt$ShortToDouble((short)51));
-    assertDoubleEquals(-51D, $opt$ShortToDouble((short)-51));
-    assertDoubleEquals(32767D, $opt$ShortToDouble((short)32767));  // 2^15 - 1
-    assertDoubleEquals(-32767D, $opt$ShortToDouble((short)-32767));  // -(2^15 - 1)
-    assertDoubleEquals(-32768D, $opt$ShortToDouble((short)-32768));  // -(2^15)
+    assertDoubleEquals(1D, $opt$noinline$ShortToDouble((short)1));
+    assertDoubleEquals(0D, $opt$noinline$ShortToDouble((short)0));
+    assertDoubleEquals(-1D, $opt$noinline$ShortToDouble((short)-1));
+    assertDoubleEquals(51D, $opt$noinline$ShortToDouble((short)51));
+    assertDoubleEquals(-51D, $opt$noinline$ShortToDouble((short)-51));
+    assertDoubleEquals(32767D, $opt$noinline$ShortToDouble((short)32767));  // 2^15 - 1
+    assertDoubleEquals(-32767D, $opt$noinline$ShortToDouble((short)-32767));  // -(2^15 - 1)
+    assertDoubleEquals(-32768D, $opt$noinline$ShortToDouble((short)-32768));  // -(2^15)
   }
 
   private static void intToDouble() {
-    assertDoubleEquals(1D, $opt$IntToDouble(1));
-    assertDoubleEquals(0D, $opt$IntToDouble(0));
-    assertDoubleEquals(-1D, $opt$IntToDouble(-1));
-    assertDoubleEquals(51D, $opt$IntToDouble(51));
-    assertDoubleEquals(-51D, $opt$IntToDouble(-51));
-    assertDoubleEquals(16777216D, $opt$IntToDouble(16777216));  // 2^24
-    assertDoubleEquals(-16777216D, $opt$IntToDouble(-16777216));  // -(2^24)
-    assertDoubleEquals(2147483647D, $opt$IntToDouble(2147483647));  // 2^31 - 1
-    assertDoubleEquals(-2147483648D, $opt$IntToDouble(-2147483648));  // -(2^31)
+    assertDoubleEquals(1D, $opt$noinline$IntToDouble(1));
+    assertDoubleEquals(0D, $opt$noinline$IntToDouble(0));
+    assertDoubleEquals(-1D, $opt$noinline$IntToDouble(-1));
+    assertDoubleEquals(51D, $opt$noinline$IntToDouble(51));
+    assertDoubleEquals(-51D, $opt$noinline$IntToDouble(-51));
+    assertDoubleEquals(16777216D, $opt$noinline$IntToDouble(16777216));  // 2^24
+    assertDoubleEquals(-16777216D, $opt$noinline$IntToDouble(-16777216));  // -(2^24)
+    assertDoubleEquals(2147483647D, $opt$noinline$IntToDouble(2147483647));  // 2^31 - 1
+    assertDoubleEquals(-2147483648D, $opt$noinline$IntToDouble(-2147483648));  // -(2^31)
   }
 
   private static void charToDouble() {
-    assertDoubleEquals(1D, $opt$CharToDouble((char)1));
-    assertDoubleEquals(0D, $opt$CharToDouble((char)0));
-    assertDoubleEquals(51D, $opt$CharToDouble((char)51));
-    assertDoubleEquals(32767D, $opt$CharToDouble((char)32767));  // 2^15 - 1
-    assertDoubleEquals(65535D, $opt$CharToDouble((char)65535));  // 2^16 - 1
-    assertDoubleEquals(65535D, $opt$CharToDouble((char)-1));
-    assertDoubleEquals(65485D, $opt$CharToDouble((char)-51));
-    assertDoubleEquals(32769D, $opt$CharToDouble((char)-32767));  // -(2^15 - 1)
-    assertDoubleEquals(32768D, $opt$CharToDouble((char)-32768));  // -(2^15)
+    assertDoubleEquals(1D, $opt$noinline$CharToDouble((char)1));
+    assertDoubleEquals(0D, $opt$noinline$CharToDouble((char)0));
+    assertDoubleEquals(51D, $opt$noinline$CharToDouble((char)51));
+    assertDoubleEquals(32767D, $opt$noinline$CharToDouble((char)32767));  // 2^15 - 1
+    assertDoubleEquals(65535D, $opt$noinline$CharToDouble((char)65535));  // 2^16 - 1
+    assertDoubleEquals(65535D, $opt$noinline$CharToDouble((char)-1));
+    assertDoubleEquals(65485D, $opt$noinline$CharToDouble((char)-51));
+    assertDoubleEquals(32769D, $opt$noinline$CharToDouble((char)-32767));  // -(2^15 - 1)
+    assertDoubleEquals(32768D, $opt$noinline$CharToDouble((char)-32768));  // -(2^15)
   }
 
   private static void longToInt() {
-    assertIntEquals(1, $opt$LongToInt(1L));
-    assertIntEquals(0, $opt$LongToInt(0L));
-    assertIntEquals(-1, $opt$LongToInt(-1L));
-    assertIntEquals(51, $opt$LongToInt(51L));
-    assertIntEquals(-51, $opt$LongToInt(-51L));
-    assertIntEquals(2147483647, $opt$LongToInt(2147483647L));  // 2^31 - 1
-    assertIntEquals(-2147483647, $opt$LongToInt(-2147483647L));  // -(2^31 - 1)
-    assertIntEquals(-2147483648, $opt$LongToInt(-2147483648L));  // -(2^31)
-    assertIntEquals(-2147483648, $opt$LongToInt(2147483648L));  // (2^31)
-    assertIntEquals(2147483647, $opt$LongToInt(-2147483649L));  // -(2^31 + 1)
-    assertIntEquals(-1, $opt$LongToInt(9223372036854775807L));  // 2^63 - 1
-    assertIntEquals(1, $opt$LongToInt(-9223372036854775807L));  // -(2^63 - 1)
-    assertIntEquals(0, $opt$LongToInt(-9223372036854775808L));  // -(2^63)
+    assertIntEquals(1, $opt$noinline$LongToInt(1L));
+    assertIntEquals(0, $opt$noinline$LongToInt(0L));
+    assertIntEquals(-1, $opt$noinline$LongToInt(-1L));
+    assertIntEquals(51, $opt$noinline$LongToInt(51L));
+    assertIntEquals(-51, $opt$noinline$LongToInt(-51L));
+    assertIntEquals(2147483647, $opt$noinline$LongToInt(2147483647L));  // 2^31 - 1
+    assertIntEquals(-2147483647, $opt$noinline$LongToInt(-2147483647L));  // -(2^31 - 1)
+    assertIntEquals(-2147483648, $opt$noinline$LongToInt(-2147483648L));  // -(2^31)
+    assertIntEquals(-2147483648, $opt$noinline$LongToInt(2147483648L));  // (2^31)
+    assertIntEquals(2147483647, $opt$noinline$LongToInt(-2147483649L));  // -(2^31 + 1)
+    assertIntEquals(-1, $opt$noinline$LongToInt(9223372036854775807L));  // 2^63 - 1
+    assertIntEquals(1, $opt$noinline$LongToInt(-9223372036854775807L));  // -(2^63 - 1)
+    assertIntEquals(0, $opt$noinline$LongToInt(-9223372036854775808L));  // -(2^63)
 
-    assertIntEquals(42, $opt$LongLiteralToInt());
+    assertIntEquals(42, $opt$noinline$LongLiteralToInt());
 
     // Ensure long-to-int conversions truncates values as expected.
-    assertLongEquals(1L, $opt$IntToLong($opt$LongToInt(4294967297L)));  // 2^32 + 1
-    assertLongEquals(0L, $opt$IntToLong($opt$LongToInt(4294967296L)));  // 2^32
-    assertLongEquals(-1L, $opt$IntToLong($opt$LongToInt(4294967295L)));  // 2^32 - 1
-    assertLongEquals(0L, $opt$IntToLong($opt$LongToInt(0L)));
-    assertLongEquals(1L, $opt$IntToLong($opt$LongToInt(-4294967295L)));  // -(2^32 - 1)
-    assertLongEquals(0L, $opt$IntToLong($opt$LongToInt(-4294967296L)));  // -(2^32)
-    assertLongEquals(-1, $opt$IntToLong($opt$LongToInt(-4294967297L)));  // -(2^32 + 1)
+    assertLongEquals(1L, $opt$noinline$IntToLong($opt$noinline$LongToInt(4294967297L)));  // 2^32 + 1
+    assertLongEquals(0L, $opt$noinline$IntToLong($opt$noinline$LongToInt(4294967296L)));  // 2^32
+    assertLongEquals(-1L, $opt$noinline$IntToLong($opt$noinline$LongToInt(4294967295L)));  // 2^32 - 1
+    assertLongEquals(0L, $opt$noinline$IntToLong($opt$noinline$LongToInt(0L)));
+    assertLongEquals(1L, $opt$noinline$IntToLong($opt$noinline$LongToInt(-4294967295L)));  // -(2^32 - 1)
+    assertLongEquals(0L, $opt$noinline$IntToLong($opt$noinline$LongToInt(-4294967296L)));  // -(2^32)
+    assertLongEquals(-1, $opt$noinline$IntToLong($opt$noinline$LongToInt(-4294967297L)));  // -(2^32 + 1)
   }
 
   private static void longToFloat() {
-    assertFloatEquals(1F, $opt$LongToFloat(1L));
-    assertFloatEquals(0F, $opt$LongToFloat(0L));
-    assertFloatEquals(-1F, $opt$LongToFloat(-1L));
-    assertFloatEquals(51F, $opt$LongToFloat(51L));
-    assertFloatEquals(-51F, $opt$LongToFloat(-51L));
-    assertFloatEquals(2147483647F, $opt$LongToFloat(2147483647L));  // 2^31 - 1
-    assertFloatEquals(-2147483647F, $opt$LongToFloat(-2147483647L));  // -(2^31 - 1)
-    assertFloatEquals(-2147483648F, $opt$LongToFloat(-2147483648L));  // -(2^31)
-    assertFloatEquals(2147483648F, $opt$LongToFloat(2147483648L));  // (2^31)
-    assertFloatEquals(-2147483649F, $opt$LongToFloat(-2147483649L));  // -(2^31 + 1)
-    assertFloatEquals(4294967296F, $opt$LongToFloat(4294967296L));  // (2^32)
-    assertFloatEquals(-4294967296F, $opt$LongToFloat(-4294967296L));  // -(2^32)
-    assertFloatEquals(140739635871745F, $opt$LongToFloat(140739635871745L));  // 1 + 2^15 + 2^31 + 2^47
-    assertFloatEquals(-140739635871745F, $opt$LongToFloat(-140739635871745L));  // -(1 + 2^15 + 2^31 + 2^47)
-    assertFloatEquals(9223372036854775807F, $opt$LongToFloat(9223372036854775807L));  // 2^63 - 1
-    assertFloatEquals(-9223372036854775807F, $opt$LongToFloat(-9223372036854775807L));  // -(2^63 - 1)
-    assertFloatEquals(-9223372036854775808F, $opt$LongToFloat(-9223372036854775808L));  // -(2^63)
+    assertFloatEquals(1F, $opt$noinline$LongToFloat(1L));
+    assertFloatEquals(0F, $opt$noinline$LongToFloat(0L));
+    assertFloatEquals(-1F, $opt$noinline$LongToFloat(-1L));
+    assertFloatEquals(51F, $opt$noinline$LongToFloat(51L));
+    assertFloatEquals(-51F, $opt$noinline$LongToFloat(-51L));
+    assertFloatEquals(2147483647F, $opt$noinline$LongToFloat(2147483647L));  // 2^31 - 1
+    assertFloatEquals(-2147483647F, $opt$noinline$LongToFloat(-2147483647L));  // -(2^31 - 1)
+    assertFloatEquals(-2147483648F, $opt$noinline$LongToFloat(-2147483648L));  // -(2^31)
+    assertFloatEquals(2147483648F, $opt$noinline$LongToFloat(2147483648L));  // (2^31)
+    assertFloatEquals(-2147483649F, $opt$noinline$LongToFloat(-2147483649L));  // -(2^31 + 1)
+    assertFloatEquals(4294967296F, $opt$noinline$LongToFloat(4294967296L));  // (2^32)
+    assertFloatEquals(-4294967296F, $opt$noinline$LongToFloat(-4294967296L));  // -(2^32)
+    assertFloatEquals(140739635871745F, $opt$noinline$LongToFloat(140739635871745L));  // 1 + 2^15 + 2^31 + 2^47
+    assertFloatEquals(-140739635871745F, $opt$noinline$LongToFloat(-140739635871745L));  // -(1 + 2^15 + 2^31 + 2^47)
+    assertFloatEquals(9223372036854775807F, $opt$noinline$LongToFloat(9223372036854775807L));  // 2^63 - 1
+    assertFloatEquals(-9223372036854775807F, $opt$noinline$LongToFloat(-9223372036854775807L));  // -(2^63 - 1)
+    assertFloatEquals(-9223372036854775808F, $opt$noinline$LongToFloat(-9223372036854775808L));  // -(2^63)
   }
 
   private static void longToDouble() {
-    assertDoubleEquals(1D, $opt$LongToDouble(1L));
-    assertDoubleEquals(0D, $opt$LongToDouble(0L));
-    assertDoubleEquals(-1D, $opt$LongToDouble(-1L));
-    assertDoubleEquals(51D, $opt$LongToDouble(51L));
-    assertDoubleEquals(-51D, $opt$LongToDouble(-51L));
-    assertDoubleEquals(2147483647D, $opt$LongToDouble(2147483647L));  // 2^31 - 1
-    assertDoubleEquals(-2147483647D, $opt$LongToDouble(-2147483647L));  // -(2^31 - 1)
-    assertDoubleEquals(-2147483648D, $opt$LongToDouble(-2147483648L));  // -(2^31)
-    assertDoubleEquals(2147483648D, $opt$LongToDouble(2147483648L));  // (2^31)
-    assertDoubleEquals(-2147483649D, $opt$LongToDouble(-2147483649L));  // -(2^31 + 1)
-    assertDoubleEquals(4294967296D, $opt$LongToDouble(4294967296L));  // (2^32)
-    assertDoubleEquals(-4294967296D, $opt$LongToDouble(-4294967296L));  // -(2^32)
-    assertDoubleEquals(140739635871745D, $opt$LongToDouble(140739635871745L));  // 1 + 2^15 + 2^31 + 2^47
-    assertDoubleEquals(-140739635871745D, $opt$LongToDouble(-140739635871745L));  // -(1 + 2^15 + 2^31 + 2^47)
-    assertDoubleEquals(9223372036854775807D, $opt$LongToDouble(9223372036854775807L));  // 2^63 - 1
-    assertDoubleEquals(-9223372036854775807D, $opt$LongToDouble(-9223372036854775807L));  // -(2^63 - 1)
-    assertDoubleEquals(-9223372036854775808D, $opt$LongToDouble(-9223372036854775808L));  // -(2^63)
+    assertDoubleEquals(1D, $opt$noinline$LongToDouble(1L));
+    assertDoubleEquals(0D, $opt$noinline$LongToDouble(0L));
+    assertDoubleEquals(-1D, $opt$noinline$LongToDouble(-1L));
+    assertDoubleEquals(51D, $opt$noinline$LongToDouble(51L));
+    assertDoubleEquals(-51D, $opt$noinline$LongToDouble(-51L));
+    assertDoubleEquals(2147483647D, $opt$noinline$LongToDouble(2147483647L));  // 2^31 - 1
+    assertDoubleEquals(-2147483647D, $opt$noinline$LongToDouble(-2147483647L));  // -(2^31 - 1)
+    assertDoubleEquals(-2147483648D, $opt$noinline$LongToDouble(-2147483648L));  // -(2^31)
+    assertDoubleEquals(2147483648D, $opt$noinline$LongToDouble(2147483648L));  // (2^31)
+    assertDoubleEquals(-2147483649D, $opt$noinline$LongToDouble(-2147483649L));  // -(2^31 + 1)
+    assertDoubleEquals(4294967296D, $opt$noinline$LongToDouble(4294967296L));  // (2^32)
+    assertDoubleEquals(-4294967296D, $opt$noinline$LongToDouble(-4294967296L));  // -(2^32)
+    assertDoubleEquals(140739635871745D, $opt$noinline$LongToDouble(140739635871745L));  // 1 + 2^15 + 2^31 + 2^47
+    assertDoubleEquals(-140739635871745D, $opt$noinline$LongToDouble(-140739635871745L));  // -(1 + 2^15 + 2^31 + 2^47)
+    assertDoubleEquals(9223372036854775807D, $opt$noinline$LongToDouble(9223372036854775807L));  // 2^63 - 1
+    assertDoubleEquals(-9223372036854775807D, $opt$noinline$LongToDouble(-9223372036854775807L));  // -(2^63 - 1)
+    assertDoubleEquals(-9223372036854775808D, $opt$noinline$LongToDouble(-9223372036854775808L));  // -(2^63)
   }
 
   private static void floatToInt() {
-    assertIntEquals(1, $opt$FloatToInt(1F));
-    assertIntEquals(0, $opt$FloatToInt(0F));
-    assertIntEquals(0, $opt$FloatToInt(-0F));
-    assertIntEquals(-1, $opt$FloatToInt(-1F));
-    assertIntEquals(51, $opt$FloatToInt(51F));
-    assertIntEquals(-51, $opt$FloatToInt(-51F));
-    assertIntEquals(0, $opt$FloatToInt(0.5F));
-    assertIntEquals(0, $opt$FloatToInt(0.4999999F));
-    assertIntEquals(0, $opt$FloatToInt(-0.4999999F));
-    assertIntEquals(0, $opt$FloatToInt(-0.5F));
-    assertIntEquals(42, $opt$FloatToInt(42.199F));
-    assertIntEquals(-42, $opt$FloatToInt(-42.199F));
-    assertIntEquals(2147483647, $opt$FloatToInt(2147483647F));  // 2^31 - 1
-    assertIntEquals(-2147483648, $opt$FloatToInt(-2147483647F));  // -(2^31 - 1)
-    assertIntEquals(-2147483648, $opt$FloatToInt(-2147483648F));  // -(2^31)
-    assertIntEquals(2147483647, $opt$FloatToInt(2147483648F));  // (2^31)
-    assertIntEquals(-2147483648, $opt$FloatToInt(-2147483649F));  // -(2^31 + 1)
-    assertIntEquals(2147483647, $opt$FloatToInt(9223372036854775807F));  // 2^63 - 1
-    assertIntEquals(-2147483648, $opt$FloatToInt(-9223372036854775807F));  // -(2^63 - 1)
-    assertIntEquals(-2147483648, $opt$FloatToInt(-9223372036854775808F));  // -(2^63)
-    assertIntEquals(0, $opt$FloatToInt(Float.NaN));
-    assertIntEquals(2147483647, $opt$FloatToInt(Float.POSITIVE_INFINITY));
-    assertIntEquals(-2147483648, $opt$FloatToInt(Float.NEGATIVE_INFINITY));
+    assertIntEquals(1, $opt$noinline$FloatToInt(1F));
+    assertIntEquals(0, $opt$noinline$FloatToInt(0F));
+    assertIntEquals(0, $opt$noinline$FloatToInt(-0F));
+    assertIntEquals(-1, $opt$noinline$FloatToInt(-1F));
+    assertIntEquals(51, $opt$noinline$FloatToInt(51F));
+    assertIntEquals(-51, $opt$noinline$FloatToInt(-51F));
+    assertIntEquals(0, $opt$noinline$FloatToInt(0.5F));
+    assertIntEquals(0, $opt$noinline$FloatToInt(0.4999999F));
+    assertIntEquals(0, $opt$noinline$FloatToInt(-0.4999999F));
+    assertIntEquals(0, $opt$noinline$FloatToInt(-0.5F));
+    assertIntEquals(42, $opt$noinline$FloatToInt(42.199F));
+    assertIntEquals(-42, $opt$noinline$FloatToInt(-42.199F));
+    assertIntEquals(2147483647, $opt$noinline$FloatToInt(2147483647F));  // 2^31 - 1
+    assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-2147483647F));  // -(2^31 - 1)
+    assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-2147483648F));  // -(2^31)
+    assertIntEquals(2147483647, $opt$noinline$FloatToInt(2147483648F));  // (2^31)
+    assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-2147483649F));  // -(2^31 + 1)
+    assertIntEquals(2147483647, $opt$noinline$FloatToInt(9223372036854775807F));  // 2^63 - 1
+    assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-9223372036854775807F));  // -(2^63 - 1)
+    assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-9223372036854775808F));  // -(2^63)
+    assertIntEquals(0, $opt$noinline$FloatToInt(Float.NaN));
+    assertIntEquals(2147483647, $opt$noinline$FloatToInt(Float.POSITIVE_INFINITY));
+    assertIntEquals(-2147483648, $opt$noinline$FloatToInt(Float.NEGATIVE_INFINITY));
   }
 
   private static void floatToLong() {
-    assertLongEquals(1L, $opt$FloatToLong(1F));
-    assertLongEquals(0L, $opt$FloatToLong(0F));
-    assertLongEquals(0L, $opt$FloatToLong(-0F));
-    assertLongEquals(-1L, $opt$FloatToLong(-1F));
-    assertLongEquals(51L, $opt$FloatToLong(51F));
-    assertLongEquals(-51L, $opt$FloatToLong(-51F));
-    assertLongEquals(0L, $opt$FloatToLong(0.5F));
-    assertLongEquals(0L, $opt$FloatToLong(0.4999999F));
-    assertLongEquals(0L, $opt$FloatToLong(-0.4999999F));
-    assertLongEquals(0L, $opt$FloatToLong(-0.5F));
-    assertLongEquals(42L, $opt$FloatToLong(42.199F));
-    assertLongEquals(-42L, $opt$FloatToLong(-42.199F));
-    assertLongEquals(2147483648L, $opt$FloatToLong(2147483647F));  // 2^31 - 1
-    assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483647F));  // -(2^31 - 1)
-    assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483648F));  // -(2^31)
-    assertLongEquals(2147483648L, $opt$FloatToLong(2147483648F));  // (2^31)
-    assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483649F));  // -(2^31 + 1)
-    assertLongEquals(9223372036854775807L, $opt$FloatToLong(9223372036854775807F));  // 2^63 - 1
-    assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775807F));  // -(2^63 - 1)
-    assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775808F));  // -(2^63)
-    assertLongEquals(0L, $opt$FloatToLong(Float.NaN));
-    assertLongEquals(9223372036854775807L, $opt$FloatToLong(Float.POSITIVE_INFINITY));
-    assertLongEquals(-9223372036854775808L, $opt$FloatToLong(Float.NEGATIVE_INFINITY));
+    assertLongEquals(1L, $opt$noinline$FloatToLong(1F));
+    assertLongEquals(0L, $opt$noinline$FloatToLong(0F));
+    assertLongEquals(0L, $opt$noinline$FloatToLong(-0F));
+    assertLongEquals(-1L, $opt$noinline$FloatToLong(-1F));
+    assertLongEquals(51L, $opt$noinline$FloatToLong(51F));
+    assertLongEquals(-51L, $opt$noinline$FloatToLong(-51F));
+    assertLongEquals(0L, $opt$noinline$FloatToLong(0.5F));
+    assertLongEquals(0L, $opt$noinline$FloatToLong(0.4999999F));
+    assertLongEquals(0L, $opt$noinline$FloatToLong(-0.4999999F));
+    assertLongEquals(0L, $opt$noinline$FloatToLong(-0.5F));
+    assertLongEquals(42L, $opt$noinline$FloatToLong(42.199F));
+    assertLongEquals(-42L, $opt$noinline$FloatToLong(-42.199F));
+    assertLongEquals(2147483648L, $opt$noinline$FloatToLong(2147483647F));  // 2^31 - 1
+    assertLongEquals(-2147483648L, $opt$noinline$FloatToLong(-2147483647F));  // -(2^31 - 1)
+    assertLongEquals(-2147483648L, $opt$noinline$FloatToLong(-2147483648F));  // -(2^31)
+    assertLongEquals(2147483648L, $opt$noinline$FloatToLong(2147483648F));  // (2^31)
+    assertLongEquals(-2147483648L, $opt$noinline$FloatToLong(-2147483649F));  // -(2^31 + 1)
+    assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(9223372036854775807F));  // 2^63 - 1
+    assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775807F));  // -(2^63 - 1)
+    assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775808F));  // -(2^63)
+    assertLongEquals(0L, $opt$noinline$FloatToLong(Float.NaN));
+    assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(Float.POSITIVE_INFINITY));
+    assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(Float.NEGATIVE_INFINITY));
   }
 
   private static void floatToDouble() {
-    assertDoubleEquals(1D, $opt$FloatToDouble(1F));
-    assertDoubleEquals(0D, $opt$FloatToDouble(0F));
-    assertDoubleEquals(0D, $opt$FloatToDouble(-0F));
-    assertDoubleEquals(-1D, $opt$FloatToDouble(-1F));
-    assertDoubleEquals(51D, $opt$FloatToDouble(51F));
-    assertDoubleEquals(-51D, $opt$FloatToDouble(-51F));
-    assertDoubleEquals(0.5D, $opt$FloatToDouble(0.5F));
-    assertDoubleEquals(0.49999991059303284D, $opt$FloatToDouble(0.4999999F));
-    assertDoubleEquals(-0.49999991059303284D, $opt$FloatToDouble(-0.4999999F));
-    assertDoubleEquals(-0.5D, $opt$FloatToDouble(-0.5F));
-    assertDoubleEquals(42.19900131225586D, $opt$FloatToDouble(42.199F));
-    assertDoubleEquals(-42.19900131225586D, $opt$FloatToDouble(-42.199F));
-    assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483647F));  // 2^31 - 1
-    assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483647F));  // -(2^31 - 1)
-    assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483648F));  // -(2^31)
-    assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483648F));  // (2^31)
-    assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483649F));  // -(2^31 + 1)
-    assertDoubleEquals(9223372036854775807D, $opt$FloatToDouble(9223372036854775807F));  // 2^63 - 1
-    assertDoubleEquals(-9223372036854775807D, $opt$FloatToDouble(-9223372036854775807F));  // -(2^63 - 1)
-    assertDoubleEquals(-9223372036854775808D, $opt$FloatToDouble(-9223372036854775808F));  // -(2^63)
-    assertDoubleIsNaN($opt$FloatToDouble(Float.NaN));
-    assertDoubleEquals(Double.POSITIVE_INFINITY, $opt$FloatToDouble(Float.POSITIVE_INFINITY));
-    assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$FloatToDouble(Float.NEGATIVE_INFINITY));
+    assertDoubleEquals(1D, $opt$noinline$FloatToDouble(1F));
+    assertDoubleEquals(0D, $opt$noinline$FloatToDouble(0F));
+    assertDoubleEquals(0D, $opt$noinline$FloatToDouble(-0F));
+    assertDoubleEquals(-1D, $opt$noinline$FloatToDouble(-1F));
+    assertDoubleEquals(51D, $opt$noinline$FloatToDouble(51F));
+    assertDoubleEquals(-51D, $opt$noinline$FloatToDouble(-51F));
+    assertDoubleEquals(0.5D, $opt$noinline$FloatToDouble(0.5F));
+    assertDoubleEquals(0.49999991059303284D, $opt$noinline$FloatToDouble(0.4999999F));
+    assertDoubleEquals(-0.49999991059303284D, $opt$noinline$FloatToDouble(-0.4999999F));
+    assertDoubleEquals(-0.5D, $opt$noinline$FloatToDouble(-0.5F));
+    assertDoubleEquals(42.19900131225586D, $opt$noinline$FloatToDouble(42.199F));
+    assertDoubleEquals(-42.19900131225586D, $opt$noinline$FloatToDouble(-42.199F));
+    assertDoubleEquals(2147483648D, $opt$noinline$FloatToDouble(2147483647F));  // 2^31 - 1
+    assertDoubleEquals(-2147483648D, $opt$noinline$FloatToDouble(-2147483647F));  // -(2^31 - 1)
+    assertDoubleEquals(-2147483648D, $opt$noinline$FloatToDouble(-2147483648F));  // -(2^31)
+    assertDoubleEquals(2147483648D, $opt$noinline$FloatToDouble(2147483648F));  // (2^31)
+    assertDoubleEquals(-2147483648D, $opt$noinline$FloatToDouble(-2147483649F));  // -(2^31 + 1)
+    assertDoubleEquals(9223372036854775807D, $opt$noinline$FloatToDouble(9223372036854775807F));  // 2^63 - 1
+    assertDoubleEquals(-9223372036854775807D, $opt$noinline$FloatToDouble(-9223372036854775807F));  // -(2^63 - 1)
+    assertDoubleEquals(-9223372036854775808D, $opt$noinline$FloatToDouble(-9223372036854775808F));  // -(2^63)
+    assertDoubleIsNaN($opt$noinline$FloatToDouble(Float.NaN));
+    assertDoubleEquals(Double.POSITIVE_INFINITY, $opt$noinline$FloatToDouble(Float.POSITIVE_INFINITY));
+    assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$noinline$FloatToDouble(Float.NEGATIVE_INFINITY));
   }
 
   private static void doubleToInt() {
-    assertIntEquals(1, $opt$DoubleToInt(1D));
-    assertIntEquals(0, $opt$DoubleToInt(0D));
-    assertIntEquals(0, $opt$DoubleToInt(-0D));
-    assertIntEquals(-1, $opt$DoubleToInt(-1D));
-    assertIntEquals(51, $opt$DoubleToInt(51D));
-    assertIntEquals(-51, $opt$DoubleToInt(-51D));
-    assertIntEquals(0, $opt$DoubleToInt(0.5D));
-    assertIntEquals(0, $opt$DoubleToInt(0.4999999D));
-    assertIntEquals(0, $opt$DoubleToInt(-0.4999999D));
-    assertIntEquals(0, $opt$DoubleToInt(-0.5D));
-    assertIntEquals(42, $opt$DoubleToInt(42.199D));
-    assertIntEquals(-42, $opt$DoubleToInt(-42.199D));
-    assertIntEquals(2147483647, $opt$DoubleToInt(2147483647D));  // 2^31 - 1
-    assertIntEquals(-2147483647, $opt$DoubleToInt(-2147483647D));  // -(2^31 - 1)
-    assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483648D));  // -(2^31)
-    assertIntEquals(2147483647, $opt$DoubleToInt(2147483648D));  // (2^31)
-    assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483649D));  // -(2^31 + 1)
-    assertIntEquals(2147483647, $opt$DoubleToInt(9223372036854775807D));  // 2^63 - 1
-    assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775807D));  // -(2^63 - 1)
-    assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775808D));  // -(2^63)
-    assertIntEquals(0, $opt$DoubleToInt(Double.NaN));
-    assertIntEquals(2147483647, $opt$DoubleToInt(Double.POSITIVE_INFINITY));
-    assertIntEquals(-2147483648, $opt$DoubleToInt(Double.NEGATIVE_INFINITY));
+    assertIntEquals(1, $opt$noinline$DoubleToInt(1D));
+    assertIntEquals(0, $opt$noinline$DoubleToInt(0D));
+    assertIntEquals(0, $opt$noinline$DoubleToInt(-0D));
+    assertIntEquals(-1, $opt$noinline$DoubleToInt(-1D));
+    assertIntEquals(51, $opt$noinline$DoubleToInt(51D));
+    assertIntEquals(-51, $opt$noinline$DoubleToInt(-51D));
+    assertIntEquals(0, $opt$noinline$DoubleToInt(0.5D));
+    assertIntEquals(0, $opt$noinline$DoubleToInt(0.4999999D));
+    assertIntEquals(0, $opt$noinline$DoubleToInt(-0.4999999D));
+    assertIntEquals(0, $opt$noinline$DoubleToInt(-0.5D));
+    assertIntEquals(42, $opt$noinline$DoubleToInt(42.199D));
+    assertIntEquals(-42, $opt$noinline$DoubleToInt(-42.199D));
+    assertIntEquals(2147483647, $opt$noinline$DoubleToInt(2147483647D));  // 2^31 - 1
+    assertIntEquals(-2147483647, $opt$noinline$DoubleToInt(-2147483647D));  // -(2^31 - 1)
+    assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-2147483648D));  // -(2^31)
+    assertIntEquals(2147483647, $opt$noinline$DoubleToInt(2147483648D));  // (2^31)
+    assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-2147483649D));  // -(2^31 + 1)
+    assertIntEquals(2147483647, $opt$noinline$DoubleToInt(9223372036854775807D));  // 2^63 - 1
+    assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-9223372036854775807D));  // -(2^63 - 1)
+    assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-9223372036854775808D));  // -(2^63)
+    assertIntEquals(0, $opt$noinline$DoubleToInt(Double.NaN));
+    assertIntEquals(2147483647, $opt$noinline$DoubleToInt(Double.POSITIVE_INFINITY));
+    assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(Double.NEGATIVE_INFINITY));
   }
 
   private static void doubleToLong() {
-    assertLongEquals(1L, $opt$DoubleToLong(1D));
-    assertLongEquals(0L, $opt$DoubleToLong(0D));
-    assertLongEquals(0L, $opt$DoubleToLong(-0D));
-    assertLongEquals(-1L, $opt$DoubleToLong(-1D));
-    assertLongEquals(51L, $opt$DoubleToLong(51D));
-    assertLongEquals(-51L, $opt$DoubleToLong(-51D));
-    assertLongEquals(0L, $opt$DoubleToLong(0.5D));
-    assertLongEquals(0L, $opt$DoubleToLong(0.4999999D));
-    assertLongEquals(0L, $opt$DoubleToLong(-0.4999999D));
-    assertLongEquals(0L, $opt$DoubleToLong(-0.5D));
-    assertLongEquals(42L, $opt$DoubleToLong(42.199D));
-    assertLongEquals(-42L, $opt$DoubleToLong(-42.199D));
-    assertLongEquals(2147483647L, $opt$DoubleToLong(2147483647D));  // 2^31 - 1
-    assertLongEquals(-2147483647L, $opt$DoubleToLong(-2147483647D));  // -(2^31 - 1)
-    assertLongEquals(-2147483648L, $opt$DoubleToLong(-2147483648D));  // -(2^31)
-    assertLongEquals(2147483648L, $opt$DoubleToLong(2147483648D));  // (2^31)
-    assertLongEquals(-2147483649L, $opt$DoubleToLong(-2147483649D));  // -(2^31 + 1)
-    assertLongEquals(9223372036854775807L, $opt$DoubleToLong(9223372036854775807D));  // 2^63 - 1
-    assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775807D));  // -(2^63 - 1)
-    assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775808D));  // -(2^63)
-    assertLongEquals(0L, $opt$DoubleToLong(Double.NaN));
-    assertLongEquals(9223372036854775807L, $opt$DoubleToLong(Double.POSITIVE_INFINITY));
-    assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(Double.NEGATIVE_INFINITY));
+    assertLongEquals(1L, $opt$noinline$DoubleToLong(1D));
+    assertLongEquals(0L, $opt$noinline$DoubleToLong(0D));
+    assertLongEquals(0L, $opt$noinline$DoubleToLong(-0D));
+    assertLongEquals(-1L, $opt$noinline$DoubleToLong(-1D));
+    assertLongEquals(51L, $opt$noinline$DoubleToLong(51D));
+    assertLongEquals(-51L, $opt$noinline$DoubleToLong(-51D));
+    assertLongEquals(0L, $opt$noinline$DoubleToLong(0.5D));
+    assertLongEquals(0L, $opt$noinline$DoubleToLong(0.4999999D));
+    assertLongEquals(0L, $opt$noinline$DoubleToLong(-0.4999999D));
+    assertLongEquals(0L, $opt$noinline$DoubleToLong(-0.5D));
+    assertLongEquals(42L, $opt$noinline$DoubleToLong(42.199D));
+    assertLongEquals(-42L, $opt$noinline$DoubleToLong(-42.199D));
+    assertLongEquals(2147483647L, $opt$noinline$DoubleToLong(2147483647D));  // 2^31 - 1
+    assertLongEquals(-2147483647L, $opt$noinline$DoubleToLong(-2147483647D));  // -(2^31 - 1)
+    assertLongEquals(-2147483648L, $opt$noinline$DoubleToLong(-2147483648D));  // -(2^31)
+    assertLongEquals(2147483648L, $opt$noinline$DoubleToLong(2147483648D));  // (2^31)
+    assertLongEquals(-2147483649L, $opt$noinline$DoubleToLong(-2147483649D));  // -(2^31 + 1)
+    assertLongEquals(9223372036854775807L, $opt$noinline$DoubleToLong(9223372036854775807D));  // 2^63 - 1
+    assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775807D));  // -(2^63 - 1)
+    assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775808D));  // -(2^63)
+    assertLongEquals(0L, $opt$noinline$DoubleToLong(Double.NaN));
+    assertLongEquals(9223372036854775807L, $opt$noinline$DoubleToLong(Double.POSITIVE_INFINITY));
+    assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(Double.NEGATIVE_INFINITY));
   }
 
   private static void doubleToFloat() {
-    assertFloatEquals(1F, $opt$DoubleToFloat(1D));
-    assertFloatEquals(0F, $opt$DoubleToFloat(0D));
-    assertFloatEquals(0F, $opt$DoubleToFloat(-0D));
-    assertFloatEquals(-1F, $opt$DoubleToFloat(-1D));
-    assertFloatEquals(51F, $opt$DoubleToFloat(51D));
-    assertFloatEquals(-51F, $opt$DoubleToFloat(-51D));
-    assertFloatEquals(0.5F, $opt$DoubleToFloat(0.5D));
-    assertFloatEquals(0.4999999F, $opt$DoubleToFloat(0.4999999D));
-    assertFloatEquals(-0.4999999F, $opt$DoubleToFloat(-0.4999999D));
-    assertFloatEquals(-0.5F, $opt$DoubleToFloat(-0.5D));
-    assertFloatEquals(42.199F, $opt$DoubleToFloat(42.199D));
-    assertFloatEquals(-42.199F, $opt$DoubleToFloat(-42.199D));
-    assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483647D));  // 2^31 - 1
-    assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483647D));  // -(2^31 - 1)
-    assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483648D));  // -(2^31)
-    assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483648D));  // (2^31)
-    assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483649D));  // -(2^31 + 1)
-    assertFloatEquals(9223372036854775807F, $opt$DoubleToFloat(9223372036854775807D));  // 2^63 - 1
-    assertFloatEquals(-9223372036854775807F, $opt$DoubleToFloat(-9223372036854775807D));  // -(2^63 - 1)
-    assertFloatEquals(-9223372036854775808F, $opt$DoubleToFloat(-9223372036854775808D));  // -(2^63)
-    assertFloatIsNaN($opt$DoubleToFloat(Float.NaN));
-    assertFloatEquals(Float.POSITIVE_INFINITY, $opt$DoubleToFloat(Double.POSITIVE_INFINITY));
-    assertFloatEquals(Float.NEGATIVE_INFINITY, $opt$DoubleToFloat(Double.NEGATIVE_INFINITY));
+    assertFloatEquals(1F, $opt$noinline$DoubleToFloat(1D));
+    assertFloatEquals(0F, $opt$noinline$DoubleToFloat(0D));
+    assertFloatEquals(0F, $opt$noinline$DoubleToFloat(-0D));
+    assertFloatEquals(-1F, $opt$noinline$DoubleToFloat(-1D));
+    assertFloatEquals(51F, $opt$noinline$DoubleToFloat(51D));
+    assertFloatEquals(-51F, $opt$noinline$DoubleToFloat(-51D));
+    assertFloatEquals(0.5F, $opt$noinline$DoubleToFloat(0.5D));
+    assertFloatEquals(0.4999999F, $opt$noinline$DoubleToFloat(0.4999999D));
+    assertFloatEquals(-0.4999999F, $opt$noinline$DoubleToFloat(-0.4999999D));
+    assertFloatEquals(-0.5F, $opt$noinline$DoubleToFloat(-0.5D));
+    assertFloatEquals(42.199F, $opt$noinline$DoubleToFloat(42.199D));
+    assertFloatEquals(-42.199F, $opt$noinline$DoubleToFloat(-42.199D));
+    assertFloatEquals(2147483648F, $opt$noinline$DoubleToFloat(2147483647D));  // 2^31 - 1
+    assertFloatEquals(-2147483648F, $opt$noinline$DoubleToFloat(-2147483647D));  // -(2^31 - 1)
+    assertFloatEquals(-2147483648F, $opt$noinline$DoubleToFloat(-2147483648D));  // -(2^31)
+    assertFloatEquals(2147483648F, $opt$noinline$DoubleToFloat(2147483648D));  // (2^31)
+    assertFloatEquals(-2147483648F, $opt$noinline$DoubleToFloat(-2147483649D));  // -(2^31 + 1)
+    assertFloatEquals(9223372036854775807F, $opt$noinline$DoubleToFloat(9223372036854775807D));  // 2^63 - 1
+    assertFloatEquals(-9223372036854775807F, $opt$noinline$DoubleToFloat(-9223372036854775807D));  // -(2^63 - 1)
+    assertFloatEquals(-9223372036854775808F, $opt$noinline$DoubleToFloat(-9223372036854775808D));  // -(2^63)
+    assertFloatIsNaN($opt$noinline$DoubleToFloat(Float.NaN));
+    assertFloatEquals(Float.POSITIVE_INFINITY, $opt$noinline$DoubleToFloat(Double.POSITIVE_INFINITY));
+    assertFloatEquals(Float.NEGATIVE_INFINITY, $opt$noinline$DoubleToFloat(Double.NEGATIVE_INFINITY));
   }
 
   private static void shortToByte() {
-    assertByteEquals((byte)1, $opt$ShortToByte((short)1));
-    assertByteEquals((byte)0, $opt$ShortToByte((short)0));
-    assertByteEquals((byte)-1, $opt$ShortToByte((short)-1));
-    assertByteEquals((byte)51, $opt$ShortToByte((short)51));
-    assertByteEquals((byte)-51, $opt$ShortToByte((short)-51));
-    assertByteEquals((byte)127, $opt$ShortToByte((short)127));  // 2^7 - 1
-    assertByteEquals((byte)-127, $opt$ShortToByte((short)-127));  // -(2^7 - 1)
-    assertByteEquals((byte)-128, $opt$ShortToByte((short)-128));  // -(2^7)
-    assertByteEquals((byte)-128, $opt$ShortToByte((short)128));  // 2^7
-    assertByteEquals((byte)127, $opt$ShortToByte((short)-129));  // -(2^7 + 1)
-    assertByteEquals((byte)-1, $opt$ShortToByte((short)32767));  // 2^15 - 1
-    assertByteEquals((byte)0, $opt$ShortToByte((short)-32768));  // -(2^15)
+    assertByteEquals((byte)1, $opt$noinline$ShortToByte((short)1));
+    assertByteEquals((byte)0, $opt$noinline$ShortToByte((short)0));
+    assertByteEquals((byte)-1, $opt$noinline$ShortToByte((short)-1));
+    assertByteEquals((byte)51, $opt$noinline$ShortToByte((short)51));
+    assertByteEquals((byte)-51, $opt$noinline$ShortToByte((short)-51));
+    assertByteEquals((byte)127, $opt$noinline$ShortToByte((short)127));  // 2^7 - 1
+    assertByteEquals((byte)-127, $opt$noinline$ShortToByte((short)-127));  // -(2^7 - 1)
+    assertByteEquals((byte)-128, $opt$noinline$ShortToByte((short)-128));  // -(2^7)
+    assertByteEquals((byte)-128, $opt$noinline$ShortToByte((short)128));  // 2^7
+    assertByteEquals((byte)127, $opt$noinline$ShortToByte((short)-129));  // -(2^7 + 1)
+    assertByteEquals((byte)-1, $opt$noinline$ShortToByte((short)32767));  // 2^15 - 1
+    assertByteEquals((byte)0, $opt$noinline$ShortToByte((short)-32768));  // -(2^15)
   }
 
   private static void intToByte() {
-    assertByteEquals((byte)1, $opt$IntToByte(1));
-    assertByteEquals((byte)0, $opt$IntToByte(0));
-    assertByteEquals((byte)-1, $opt$IntToByte(-1));
-    assertByteEquals((byte)51, $opt$IntToByte(51));
-    assertByteEquals((byte)-51, $opt$IntToByte(-51));
-    assertByteEquals((byte)127, $opt$IntToByte(127));  // 2^7 - 1
-    assertByteEquals((byte)-127, $opt$IntToByte(-127));  // -(2^7 - 1)
-    assertByteEquals((byte)-128, $opt$IntToByte(-128));  // -(2^7)
-    assertByteEquals((byte)-128, $opt$IntToByte(128));  // 2^7
-    assertByteEquals((byte)127, $opt$IntToByte(-129));  // -(2^7 + 1)
-    assertByteEquals((byte)-1, $opt$IntToByte(2147483647));  // 2^31 - 1
-    assertByteEquals((byte)0, $opt$IntToByte(-2147483648));  // -(2^31)
+    assertByteEquals((byte)1, $opt$noinline$IntToByte(1));
+    assertByteEquals((byte)0, $opt$noinline$IntToByte(0));
+    assertByteEquals((byte)-1, $opt$noinline$IntToByte(-1));
+    assertByteEquals((byte)51, $opt$noinline$IntToByte(51));
+    assertByteEquals((byte)-51, $opt$noinline$IntToByte(-51));
+    assertByteEquals((byte)127, $opt$noinline$IntToByte(127));  // 2^7 - 1
+    assertByteEquals((byte)-127, $opt$noinline$IntToByte(-127));  // -(2^7 - 1)
+    assertByteEquals((byte)-128, $opt$noinline$IntToByte(-128));  // -(2^7)
+    assertByteEquals((byte)-128, $opt$noinline$IntToByte(128));  // 2^7
+    assertByteEquals((byte)127, $opt$noinline$IntToByte(-129));  // -(2^7 + 1)
+    assertByteEquals((byte)-1, $opt$noinline$IntToByte(2147483647));  // 2^31 - 1
+    assertByteEquals((byte)0, $opt$noinline$IntToByte(-2147483648));  // -(2^31)
   }
 
   private static void charToByte() {
-    assertByteEquals((byte)1, $opt$CharToByte((char)1));
-    assertByteEquals((byte)0, $opt$CharToByte((char)0));
-    assertByteEquals((byte)51, $opt$CharToByte((char)51));
-    assertByteEquals((byte)127, $opt$CharToByte((char)127));  // 2^7 - 1
-    assertByteEquals((byte)-128, $opt$CharToByte((char)128));  // 2^7
-    assertByteEquals((byte)-1, $opt$CharToByte((char)32767));  // 2^15 - 1
-    assertByteEquals((byte)-1, $opt$CharToByte((char)65535));  // 2^16 - 1
-    assertByteEquals((byte)-1, $opt$CharToByte((char)-1));
-    assertByteEquals((byte)-51, $opt$CharToByte((char)-51));
-    assertByteEquals((byte)-127, $opt$CharToByte((char)-127));  // -(2^7 - 1)
-    assertByteEquals((byte)-128, $opt$CharToByte((char)-128));  // -(2^7)
-    assertByteEquals((byte)127, $opt$CharToByte((char)-129));  // -(2^7 + 1)
+    assertByteEquals((byte)1, $opt$noinline$CharToByte((char)1));
+    assertByteEquals((byte)0, $opt$noinline$CharToByte((char)0));
+    assertByteEquals((byte)51, $opt$noinline$CharToByte((char)51));
+    assertByteEquals((byte)127, $opt$noinline$CharToByte((char)127));  // 2^7 - 1
+    assertByteEquals((byte)-128, $opt$noinline$CharToByte((char)128));  // 2^7
+    assertByteEquals((byte)-1, $opt$noinline$CharToByte((char)32767));  // 2^15 - 1
+    assertByteEquals((byte)-1, $opt$noinline$CharToByte((char)65535));  // 2^16 - 1
+    assertByteEquals((byte)-1, $opt$noinline$CharToByte((char)-1));
+    assertByteEquals((byte)-51, $opt$noinline$CharToByte((char)-51));
+    assertByteEquals((byte)-127, $opt$noinline$CharToByte((char)-127));  // -(2^7 - 1)
+    assertByteEquals((byte)-128, $opt$noinline$CharToByte((char)-128));  // -(2^7)
+    assertByteEquals((byte)127, $opt$noinline$CharToByte((char)-129));  // -(2^7 + 1)
   }
 
   private static void byteToShort() {
-    assertShortEquals((short)1, $opt$ByteToShort((byte)1));
-    assertShortEquals((short)0, $opt$ByteToShort((byte)0));
-    assertShortEquals((short)-1, $opt$ByteToShort((byte)-1));
-    assertShortEquals((short)51, $opt$ByteToShort((byte)51));
-    assertShortEquals((short)-51, $opt$ByteToShort((byte)-51));
-    assertShortEquals((short)127, $opt$ByteToShort((byte)127));  // 2^7 - 1
-    assertShortEquals((short)-127, $opt$ByteToShort((byte)-127));  // -(2^7 - 1)
-    assertShortEquals((short)-128, $opt$ByteToShort((byte)-128));  // -(2^7)
+    assertShortEquals((short)1, $opt$noinline$ByteToShort((byte)1));
+    assertShortEquals((short)0, $opt$noinline$ByteToShort((byte)0));
+    assertShortEquals((short)-1, $opt$noinline$ByteToShort((byte)-1));
+    assertShortEquals((short)51, $opt$noinline$ByteToShort((byte)51));
+    assertShortEquals((short)-51, $opt$noinline$ByteToShort((byte)-51));
+    assertShortEquals((short)127, $opt$noinline$ByteToShort((byte)127));  // 2^7 - 1
+    assertShortEquals((short)-127, $opt$noinline$ByteToShort((byte)-127));  // -(2^7 - 1)
+    assertShortEquals((short)-128, $opt$noinline$ByteToShort((byte)-128));  // -(2^7)
   }
 
   private static void intToShort() {
-    assertShortEquals((short)1, $opt$IntToShort(1));
-    assertShortEquals((short)0, $opt$IntToShort(0));
-    assertShortEquals((short)-1, $opt$IntToShort(-1));
-    assertShortEquals((short)51, $opt$IntToShort(51));
-    assertShortEquals((short)-51, $opt$IntToShort(-51));
-    assertShortEquals((short)32767, $opt$IntToShort(32767));  // 2^15 - 1
-    assertShortEquals((short)-32767, $opt$IntToShort(-32767));  // -(2^15 - 1)
-    assertShortEquals((short)-32768, $opt$IntToShort(-32768));  // -(2^15)
-    assertShortEquals((short)-32768, $opt$IntToShort(32768));  // 2^15
-    assertShortEquals((short)32767, $opt$IntToShort(-32769));  // -(2^15 + 1)
-    assertShortEquals((short)-1, $opt$IntToShort(2147483647));  // 2^31 - 1
-    assertShortEquals((short)0, $opt$IntToShort(-2147483648));  // -(2^31)
+    assertShortEquals((short)1, $opt$noinline$IntToShort(1));
+    assertShortEquals((short)0, $opt$noinline$IntToShort(0));
+    assertShortEquals((short)-1, $opt$noinline$IntToShort(-1));
+    assertShortEquals((short)51, $opt$noinline$IntToShort(51));
+    assertShortEquals((short)-51, $opt$noinline$IntToShort(-51));
+    assertShortEquals((short)32767, $opt$noinline$IntToShort(32767));  // 2^15 - 1
+    assertShortEquals((short)-32767, $opt$noinline$IntToShort(-32767));  // -(2^15 - 1)
+    assertShortEquals((short)-32768, $opt$noinline$IntToShort(-32768));  // -(2^15)
+    assertShortEquals((short)-32768, $opt$noinline$IntToShort(32768));  // 2^15
+    assertShortEquals((short)32767, $opt$noinline$IntToShort(-32769));  // -(2^15 + 1)
+    assertShortEquals((short)-1, $opt$noinline$IntToShort(2147483647));  // 2^31 - 1
+    assertShortEquals((short)0, $opt$noinline$IntToShort(-2147483648));  // -(2^31)
   }
 
   private static void charToShort() {
-    assertShortEquals((short)1, $opt$CharToShort((char)1));
-    assertShortEquals((short)0, $opt$CharToShort((char)0));
-    assertShortEquals((short)51, $opt$CharToShort((char)51));
-    assertShortEquals((short)32767, $opt$CharToShort((char)32767));  // 2^15 - 1
-    assertShortEquals((short)-32768, $opt$CharToShort((char)32768));  // 2^15
-    assertShortEquals((short)-32767, $opt$CharToShort((char)32769));  // 2^15
-    assertShortEquals((short)-1, $opt$CharToShort((char)65535));  // 2^16 - 1
-    assertShortEquals((short)-1, $opt$CharToShort((char)-1));
-    assertShortEquals((short)-51, $opt$CharToShort((char)-51));
-    assertShortEquals((short)-32767, $opt$CharToShort((char)-32767));  // -(2^15 - 1)
-    assertShortEquals((short)-32768, $opt$CharToShort((char)-32768));  // -(2^15)
-    assertShortEquals((short)32767, $opt$CharToShort((char)-32769));  // -(2^15 + 1)
+    assertShortEquals((short)1, $opt$noinline$CharToShort((char)1));
+    assertShortEquals((short)0, $opt$noinline$CharToShort((char)0));
+    assertShortEquals((short)51, $opt$noinline$CharToShort((char)51));
+    assertShortEquals((short)32767, $opt$noinline$CharToShort((char)32767));  // 2^15 - 1
+    assertShortEquals((short)-32768, $opt$noinline$CharToShort((char)32768));  // 2^15
+    assertShortEquals((short)-32767, $opt$noinline$CharToShort((char)32769));  // 2^15
+    assertShortEquals((short)-1, $opt$noinline$CharToShort((char)65535));  // 2^16 - 1
+    assertShortEquals((short)-1, $opt$noinline$CharToShort((char)-1));
+    assertShortEquals((short)-51, $opt$noinline$CharToShort((char)-51));
+    assertShortEquals((short)-32767, $opt$noinline$CharToShort((char)-32767));  // -(2^15 - 1)
+    assertShortEquals((short)-32768, $opt$noinline$CharToShort((char)-32768));  // -(2^15)
+    assertShortEquals((short)32767, $opt$noinline$CharToShort((char)-32769));  // -(2^15 + 1)
   }
 
   private static void byteToChar() {
-    assertCharEquals((char)1, $opt$ByteToChar((byte)1));
-    assertCharEquals((char)0, $opt$ByteToChar((byte)0));
-    assertCharEquals((char)65535, $opt$ByteToChar((byte)-1));
-    assertCharEquals((char)51, $opt$ByteToChar((byte)51));
-    assertCharEquals((char)65485, $opt$ByteToChar((byte)-51));
-    assertCharEquals((char)127, $opt$ByteToChar((byte)127));  // 2^7 - 1
-    assertCharEquals((char)65409, $opt$ByteToChar((byte)-127));  // -(2^7 - 1)
-    assertCharEquals((char)65408, $opt$ByteToChar((byte)-128));  // -(2^7)
+    assertCharEquals((char)1, $opt$noinline$ByteToChar((byte)1));
+    assertCharEquals((char)0, $opt$noinline$ByteToChar((byte)0));
+    assertCharEquals((char)65535, $opt$noinline$ByteToChar((byte)-1));
+    assertCharEquals((char)51, $opt$noinline$ByteToChar((byte)51));
+    assertCharEquals((char)65485, $opt$noinline$ByteToChar((byte)-51));
+    assertCharEquals((char)127, $opt$noinline$ByteToChar((byte)127));  // 2^7 - 1
+    assertCharEquals((char)65409, $opt$noinline$ByteToChar((byte)-127));  // -(2^7 - 1)
+    assertCharEquals((char)65408, $opt$noinline$ByteToChar((byte)-128));  // -(2^7)
   }
 
   private static void shortToChar() {
-    assertCharEquals((char)1, $opt$ShortToChar((short)1));
-    assertCharEquals((char)0, $opt$ShortToChar((short)0));
-    assertCharEquals((char)65535, $opt$ShortToChar((short)-1));
-    assertCharEquals((char)51, $opt$ShortToChar((short)51));
-    assertCharEquals((char)65485, $opt$ShortToChar((short)-51));
-    assertCharEquals((char)32767, $opt$ShortToChar((short)32767));  // 2^15 - 1
-    assertCharEquals((char)32769, $opt$ShortToChar((short)-32767));  // -(2^15 - 1)
-    assertCharEquals((char)32768, $opt$ShortToChar((short)-32768));  // -(2^15)
+    assertCharEquals((char)1, $opt$noinline$ShortToChar((short)1));
+    assertCharEquals((char)0, $opt$noinline$ShortToChar((short)0));
+    assertCharEquals((char)65535, $opt$noinline$ShortToChar((short)-1));
+    assertCharEquals((char)51, $opt$noinline$ShortToChar((short)51));
+    assertCharEquals((char)65485, $opt$noinline$ShortToChar((short)-51));
+    assertCharEquals((char)32767, $opt$noinline$ShortToChar((short)32767));  // 2^15 - 1
+    assertCharEquals((char)32769, $opt$noinline$ShortToChar((short)-32767));  // -(2^15 - 1)
+    assertCharEquals((char)32768, $opt$noinline$ShortToChar((short)-32768));  // -(2^15)
   }
 
   private static void intToChar() {
-    assertCharEquals((char)1, $opt$IntToChar(1));
-    assertCharEquals((char)0, $opt$IntToChar(0));
-    assertCharEquals((char)65535, $opt$IntToChar(-1));
-    assertCharEquals((char)51, $opt$IntToChar(51));
-    assertCharEquals((char)65485, $opt$IntToChar(-51));
-    assertCharEquals((char)32767, $opt$IntToChar(32767));  // 2^15 - 1
-    assertCharEquals((char)32769, $opt$IntToChar(-32767));  // -(2^15 - 1)
-    assertCharEquals((char)32768, $opt$IntToChar(32768));  // 2^15
-    assertCharEquals((char)32768, $opt$IntToChar(-32768));  // -(2^15)
-    assertCharEquals((char)65535, $opt$IntToChar(65535));  // 2^16 - 1
-    assertCharEquals((char)1, $opt$IntToChar(-65535));  // -(2^16 - 1)
-    assertCharEquals((char)0, $opt$IntToChar(65536));  // 2^16
-    assertCharEquals((char)0, $opt$IntToChar(-65536));  // -(2^16)
-    assertCharEquals((char)65535, $opt$IntToChar(2147483647));  // 2^31 - 1
-    assertCharEquals((char)0, $opt$IntToChar(-2147483648));  // -(2^31)
+    assertCharEquals((char)1, $opt$noinline$IntToChar(1));
+    assertCharEquals((char)0, $opt$noinline$IntToChar(0));
+    assertCharEquals((char)65535, $opt$noinline$IntToChar(-1));
+    assertCharEquals((char)51, $opt$noinline$IntToChar(51));
+    assertCharEquals((char)65485, $opt$noinline$IntToChar(-51));
+    assertCharEquals((char)32767, $opt$noinline$IntToChar(32767));  // 2^15 - 1
+    assertCharEquals((char)32769, $opt$noinline$IntToChar(-32767));  // -(2^15 - 1)
+    assertCharEquals((char)32768, $opt$noinline$IntToChar(32768));  // 2^15
+    assertCharEquals((char)32768, $opt$noinline$IntToChar(-32768));  // -(2^15)
+    assertCharEquals((char)65535, $opt$noinline$IntToChar(65535));  // 2^16 - 1
+    assertCharEquals((char)1, $opt$noinline$IntToChar(-65535));  // -(2^16 - 1)
+    assertCharEquals((char)0, $opt$noinline$IntToChar(65536));  // 2^16
+    assertCharEquals((char)0, $opt$noinline$IntToChar(-65536));  // -(2^16)
+    assertCharEquals((char)65535, $opt$noinline$IntToChar(2147483647));  // 2^31 - 1
+    assertCharEquals((char)0, $opt$noinline$IntToChar(-2147483648));  // -(2^31)
   }
 
   // A dummy value to defeat inlining of these routines.
   static boolean doThrow = false;
 
   // These methods produce int-to-long Dex instructions.
-  static long $opt$ByteToLong(byte a) { if (doThrow) throw new Error(); return (long)a; }
-  static long $opt$ShortToLong(short a) { if (doThrow) throw new Error(); return (long)a; }
-  static long $opt$IntToLong(int a) { if (doThrow) throw new Error(); return (long)a; }
-  static long $opt$CharToLong(int a) { if (doThrow) throw new Error(); return (long)a; }
+  static long $opt$noinline$ByteToLong(byte a) { if (doThrow) throw new Error(); return (long)a; }
+  static long $opt$noinline$ShortToLong(short a) { if (doThrow) throw new Error(); return (long)a; }
+  static long $opt$noinline$IntToLong(int a) { if (doThrow) throw new Error(); return (long)a; }
+  static long $opt$noinline$CharToLong(int a) { if (doThrow) throw new Error(); return (long)a; }
 
   // These methods produce int-to-float Dex instructions.
-  static float $opt$ByteToFloat(byte a) { if (doThrow) throw new Error(); return (float)a; }
-  static float $opt$ShortToFloat(short a) { if (doThrow) throw new Error(); return (float)a; }
-  static float $opt$IntToFloat(int a) { if (doThrow) throw new Error(); return (float)a; }
-  static float $opt$CharToFloat(char a) { if (doThrow) throw new Error(); return (float)a; }
+  static float $opt$noinline$ByteToFloat(byte a) { if (doThrow) throw new Error(); return (float)a; }
+  static float $opt$noinline$ShortToFloat(short a) { if (doThrow) throw new Error(); return (float)a; }
+  static float $opt$noinline$IntToFloat(int a) { if (doThrow) throw new Error(); return (float)a; }
+  static float $opt$noinline$CharToFloat(char a) { if (doThrow) throw new Error(); return (float)a; }
 
   // These methods produce int-to-double Dex instructions.
-  static double $opt$ByteToDouble(byte a) { if (doThrow) throw new Error(); return (double)a; }
-  static double $opt$ShortToDouble(short a) { if (doThrow) throw new Error(); return (double)a; }
-  static double $opt$IntToDouble(int a) { if (doThrow) throw new Error(); return (double)a; }
-  static double $opt$CharToDouble(int a) { if (doThrow) throw new Error(); return (double)a; }
+  static double $opt$noinline$ByteToDouble(byte a) { if (doThrow) throw new Error(); return (double)a; }
+  static double $opt$noinline$ShortToDouble(short a) { if (doThrow) throw new Error(); return (double)a; }
+  static double $opt$noinline$IntToDouble(int a) { if (doThrow) throw new Error(); return (double)a; }
+  static double $opt$noinline$CharToDouble(int a) { if (doThrow) throw new Error(); return (double)a; }
 
   // These methods produce long-to-int Dex instructions.
-  static int $opt$LongToInt(long a) { if (doThrow) throw new Error(); return (int)a; }
-  static int $opt$LongLiteralToInt() { if (doThrow) throw new Error(); return (int)42L; }
+  static int $opt$noinline$LongToInt(long a) { if (doThrow) throw new Error(); return (int)a; }
+  static int $opt$noinline$LongLiteralToInt() { if (doThrow) throw new Error(); return (int)42L; }
 
   // This method produces a long-to-float Dex instruction.
-  static float $opt$LongToFloat(long a) { if (doThrow) throw new Error(); return (float)a; }
+  static float $opt$noinline$LongToFloat(long a) { if (doThrow) throw new Error(); return (float)a; }
 
   // This method produces a long-to-double Dex instruction.
-  static double $opt$LongToDouble(long a) { if (doThrow) throw new Error(); return (double)a; }
+  static double $opt$noinline$LongToDouble(long a) { if (doThrow) throw new Error(); return (double)a; }
 
   // This method produces a float-to-int Dex instruction.
-  static int $opt$FloatToInt(float a) { if (doThrow) throw new Error(); return (int)a; }
+  static int $opt$noinline$FloatToInt(float a) { if (doThrow) throw new Error(); return (int)a; }
 
   // This method produces a float-to-long Dex instruction.
-  static long $opt$FloatToLong(float a){ if (doThrow) throw new Error(); return (long)a; }
+  static long $opt$noinline$FloatToLong(float a){ if (doThrow) throw new Error(); return (long)a; }
 
   // This method produces a float-to-double Dex instruction.
-  static double $opt$FloatToDouble(float a) { if (doThrow) throw new Error(); return (double)a; }
+  static double $opt$noinline$FloatToDouble(float a) { if (doThrow) throw new Error(); return (double)a; }
 
   // This method produces a double-to-int Dex instruction.
-  static int $opt$DoubleToInt(double a){ if (doThrow) throw new Error(); return (int)a; }
+  static int $opt$noinline$DoubleToInt(double a){ if (doThrow) throw new Error(); return (int)a; }
 
   // This method produces a double-to-long Dex instruction.
-  static long $opt$DoubleToLong(double a){ if (doThrow) throw new Error(); return (long)a; }
+  static long $opt$noinline$DoubleToLong(double a){ if (doThrow) throw new Error(); return (long)a; }
 
   // This method produces a double-to-float Dex instruction.
-  static float $opt$DoubleToFloat(double a) { if (doThrow) throw new Error(); return (float)a; }
+  static float $opt$noinline$DoubleToFloat(double a) { if (doThrow) throw new Error(); return (float)a; }
 
   // These methods produce int-to-byte Dex instructions.
-  static byte $opt$ShortToByte(short a) { if (doThrow) throw new Error(); return (byte)a; }
-  static byte $opt$IntToByte(int a) { if (doThrow) throw new Error(); return (byte)a; }
-  static byte $opt$CharToByte(char a) { if (doThrow) throw new Error(); return (byte)a; }
+  static byte $opt$noinline$ShortToByte(short a) { if (doThrow) throw new Error(); return (byte)a; }
+  static byte $opt$noinline$IntToByte(int a) { if (doThrow) throw new Error(); return (byte)a; }
+  static byte $opt$noinline$CharToByte(char a) { if (doThrow) throw new Error(); return (byte)a; }
 
   // These methods produce int-to-short Dex instructions.
-  static short $opt$ByteToShort(byte a) { if (doThrow) throw new Error(); return (short)a; }
-  static short $opt$IntToShort(int a) { if (doThrow) throw new Error(); return (short)a; }
-  static short $opt$CharToShort(char a) { if (doThrow) throw new Error(); return (short)a; }
+  static short $opt$noinline$ByteToShort(byte a) { if (doThrow) throw new Error(); return (short)a; }
+  static short $opt$noinline$IntToShort(int a) { if (doThrow) throw new Error(); return (short)a; }
+  static short $opt$noinline$CharToShort(char a) { if (doThrow) throw new Error(); return (short)a; }
 
   // These methods produce int-to-char Dex instructions.
-  static char $opt$ByteToChar(byte a) { if (doThrow) throw new Error(); return (char)a; }
-  static char $opt$ShortToChar(short a) { if (doThrow) throw new Error(); return (char)a; }
-  static char $opt$IntToChar(int a) { if (doThrow) throw new Error(); return (char)a; }
+  static char $opt$noinline$ByteToChar(byte a) { if (doThrow) throw new Error(); return (char)a; }
+  static char $opt$noinline$ShortToChar(short a) { if (doThrow) throw new Error(); return (char)a; }
+  static char $opt$noinline$IntToChar(int a) { if (doThrow) throw new Error(); return (char)a; }
 }
diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java
index df969a4..3899d7f 100644
--- a/test/441-checker-inliner/src/Main.java
+++ b/test/441-checker-inliner/src/Main.java
@@ -19,7 +19,7 @@
   /// CHECK-START: void Main.InlineVoid() inliner (before)
   /// CHECK-DAG:     <<Const42:i\d+>> IntConstant 42
   /// CHECK-DAG:                      InvokeStaticOrDirect
-  /// CHECK-DAG:                      InvokeStaticOrDirect [<<Const42>>]
+  /// CHECK-DAG:                      InvokeStaticOrDirect [<<Const42>>,{{[ij]\d+}}]
 
   /// CHECK-START: void Main.InlineVoid() inliner (after)
   /// CHECK-NOT:                      InvokeStaticOrDirect
@@ -31,7 +31,7 @@
 
   /// CHECK-START: int Main.InlineParameter(int) inliner (before)
   /// CHECK-DAG:     <<Param:i\d+>>  ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect [<<Param>>]
+  /// CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect [<<Param>>,{{[ij]\d+}}]
   /// CHECK-DAG:                     Return [<<Result>>]
 
   /// CHECK-START: int Main.InlineParameter(int) inliner (after)
@@ -44,7 +44,7 @@
 
   /// CHECK-START: long Main.InlineWideParameter(long) inliner (before)
   /// CHECK-DAG:     <<Param:j\d+>>  ParameterValue
-  /// CHECK-DAG:     <<Result:j\d+>> InvokeStaticOrDirect [<<Param>>]
+  /// CHECK-DAG:     <<Result:j\d+>> InvokeStaticOrDirect [<<Param>>,{{[ij]\d+}}]
   /// CHECK-DAG:                     Return [<<Result>>]
 
   /// CHECK-START: long Main.InlineWideParameter(long) inliner (after)
@@ -57,7 +57,7 @@
 
   /// CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (before)
   /// CHECK-DAG:     <<Param:l\d+>>  ParameterValue
-  /// CHECK-DAG:     <<Result:l\d+>> InvokeStaticOrDirect [<<Param>>]
+  /// CHECK-DAG:     <<Result:l\d+>> InvokeStaticOrDirect [<<Param>>,{{[ij]\d+}}]
   /// CHECK-DAG:                     Return [<<Result>>]
 
   /// CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (after)
@@ -130,8 +130,8 @@
   /// CHECK-DAG:     <<Const1:i\d+>> IntConstant 1
   /// CHECK-DAG:     <<Const3:i\d+>> IntConstant 3
   /// CHECK-DAG:     <<Const5:i\d+>> IntConstant 5
-  /// CHECK-DAG:     <<Add:i\d+>>    InvokeStaticOrDirect [<<Const1>>,<<Const3>>]
-  /// CHECK-DAG:     <<Sub:i\d+>>    InvokeStaticOrDirect [<<Const5>>,<<Const3>>]
+  /// CHECK-DAG:     <<Add:i\d+>>    InvokeStaticOrDirect [<<Const1>>,<<Const3>>,{{[ij]\d+}}]
+  /// CHECK-DAG:     <<Sub:i\d+>>    InvokeStaticOrDirect [<<Const5>>,<<Const3>>,{{[ij]\d+}}]
   /// CHECK-DAG:     <<Phi:i\d+>>    Phi [<<Add>>,<<Sub>>]
   /// CHECK-DAG:                     Return [<<Phi>>]
 
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index 9bf7cdf..4056275 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -16,25 +16,25 @@
 
 
 interface Interface {
-  void f();
+  void $noinline$f();
 }
 
 class Super implements Interface {
-  public void f() {
+  public void $noinline$f() {
     throw new RuntimeException();
   }
 }
 
 class SubclassA extends Super {
-  public void f() {
+  public void $noinline$f() {
     throw new RuntimeException();
   }
 
-  public String h() {
+  public String $noinline$h() {
     throw new RuntimeException();
   }
 
-  void g() {
+  void $noinline$g() {
     throw new RuntimeException();
   }
 }
@@ -43,11 +43,11 @@
 }
 
 class SubclassB extends Super {
-  public void f() {
+  public void $noinline$f() {
     throw new RuntimeException();
   }
 
-  void g() {
+  void $noinline$g() {
     throw new RuntimeException();
   }
 }
@@ -61,7 +61,7 @@
   /// CHECK-NOT:     CheckCast
   public void testSimpleRemove() {
     Super s = new SubclassA();
-    ((SubclassA)s).g();
+    ((SubclassA)s).$noinline$g();
   }
 
   /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (before)
@@ -70,7 +70,7 @@
   /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (after)
   /// CHECK:         CheckCast
   public void testSimpleKeep(Super s) {
-    ((SubclassA)s).f();
+    ((SubclassA)s).$noinline$f();
   }
 
   /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (before)
@@ -90,7 +90,7 @@
   /// CHECK:         CheckCast
   public String testClassKeep() {
     Object s = SubclassA.class;
-    return ((SubclassA)s).h();
+    return ((SubclassA)s).$noinline$h();
   }
 
   /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (before)
@@ -105,7 +105,7 @@
     } else {
       s = new SubclassC();
     }
-    ((SubclassA)s).g();
+    ((SubclassA)s).$noinline$g();
   }
 
   /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (before)
@@ -120,7 +120,7 @@
     } else {
       s = new SubclassB();
     }
-    ((SubclassA)s).g();
+    ((SubclassA)s).$noinline$g();
   }
 
   /// CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (before)
@@ -135,7 +135,7 @@
         s = new SubclassC();
       }
     }
-    ((SubclassA)s).g();
+    ((SubclassA)s).$noinline$g();
   }
 
   /// CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (before)
@@ -150,7 +150,7 @@
         s = new SubclassC();
       }
     }
-    ((SubclassC)s).g();
+    ((SubclassC)s).$noinline$g();
   }
 
   /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (before)
@@ -165,7 +165,7 @@
     } else {
       x = newObject();  // this one will have an unknown type.
     }
-    ((SubclassC)x).g();
+    ((SubclassC)x).$noinline$g();
   }
 
   /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (before)
@@ -176,10 +176,10 @@
   /// CHECK-NOT:     CheckCast
   public void testInstanceOf(Object o) {
     if (o instanceof SubclassC) {
-      ((SubclassC)o).g();
+      ((SubclassC)o).$noinline$g();
     }
     if (o instanceof SubclassB) {
-      ((SubclassB)o).g();
+      ((SubclassB)o).$noinline$g();
     }
   }
 
@@ -192,10 +192,10 @@
   /// CHECK:         CheckCast
   public void testInstanceOfKeep(Object o) {
     if (o instanceof SubclassC) {
-      ((SubclassB)o).g();
+      ((SubclassB)o).$noinline$g();
     }
     if (o instanceof SubclassB) {
-      ((SubclassA)o).g();
+      ((SubclassA)o).$noinline$g();
     }
   }
 
@@ -208,9 +208,9 @@
   public void testInstanceOfNested(Object o) {
     if (o instanceof SubclassC) {
       if (o instanceof SubclassB) {
-        ((SubclassB)o).g();
+        ((SubclassB)o).$noinline$g();
       } else {
-        ((SubclassC)o).g();
+        ((SubclassC)o).$noinline$g();
       }
     }
   }
@@ -229,7 +229,7 @@
     }
 
     if (o instanceof SubclassB) {
-      ((SubclassB)o).g();
+      ((SubclassB)o).$noinline$g();
     }
   }
 
@@ -245,7 +245,7 @@
         o = new SubclassB();
       }
       if (o instanceof SubclassB) {
-        ((SubclassB)o).g();
+        ((SubclassB)o).$noinline$g();
       }
     }
   }
@@ -258,7 +258,7 @@
   public void testInstanceOfSubclass() {
     Object o = new SubclassA();
     if (o instanceof Super) {
-      ((SubclassA)o).g();
+      ((SubclassA)o).$noinline$g();
     }
   }
 
@@ -276,7 +276,7 @@
     }
 
     if (o instanceof Super) {
-      ((SubclassA)o).g();
+      ((SubclassA)o).$noinline$g();
     }
   }
 
@@ -294,7 +294,7 @@
     }
 
     if (o instanceof Super) {
-      ((Super)o).f();
+      ((Super)o).$noinline$f();
     }
   }
 
@@ -307,7 +307,7 @@
     Object o = new SubclassA();
     for (int i = 0; i < n; i++) {
       if (o instanceof Super) {
-        ((SubclassA)o).g();
+        ((SubclassA)o).$noinline$g();
       }
       if (i / 2 == 0) {
         o = new SubclassC();
@@ -324,7 +324,7 @@
     Object o = new SubclassA();
     for (int i = 0; i < n; i++) {
       if (o instanceof Super) {
-        ((Super)o).f();
+        ((Super)o).$noinline$f();
       }
       if (i / 2 == 0) {
         o = new Object();
@@ -351,7 +351,7 @@
   public void testInstanceFieldGetSimpleRemove() {
     Main m = new Main();
     Super a = m.a;
-    ((SubclassA)a).g();
+    ((SubclassA)a).$noinline$g();
   }
 
   /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier_after_types (before)
@@ -361,7 +361,7 @@
   /// CHECK-NOT:     CheckCast
   public void testStaticFieldGetSimpleRemove() {
     Super b = Main.b;
-    ((SubclassA)b).g();
+    ((SubclassA)b).$noinline$g();
   }
 
   public static void main(String[] args) {
diff --git a/test/451-spill-splot/src/Main.java b/test/451-spill-splot/src/Main.java
index f631ebd..b2f39f3 100644
--- a/test/451-spill-splot/src/Main.java
+++ b/test/451-spill-splot/src/Main.java
@@ -48,37 +48,37 @@
     for (int count = 0; count < 2; count++) {
       System.out.println(aa + bb + cc + dd + ee + ff + gg + hh + ii + jj + kk + ll + mm + nn);
       System.out.println(a + b + c + d + e + f + g + h + i + j);
-      a = computeDouble();
-      b = computeDouble();
-      c = computeDouble();
-      d = computeDouble();
-      e = computeDouble();
-      f = computeDouble();
-      g = computeDouble();
-      h = computeDouble();
-      i = computeDouble();
-      j = computeDouble();
+      a = $noinline$computeDouble();
+      b = $noinline$computeDouble();
+      c = $noinline$computeDouble();
+      d = $noinline$computeDouble();
+      e = $noinline$computeDouble();
+      f = $noinline$computeDouble();
+      g = $noinline$computeDouble();
+      h = $noinline$computeDouble();
+      i = $noinline$computeDouble();
+      j = $noinline$computeDouble();
       System.out.println(a + b + c + d + e + f + g + h + i + j);
-      aa = computeFloat();
-      bb = computeFloat();
-      cc = computeFloat();
-      dd = computeFloat();
-      ee = computeFloat();
-      ff = computeFloat();
-      gg = computeFloat();
-      hh = computeFloat();
-      ii = computeFloat();
-      jj = computeFloat();
-      kk = computeFloat();
-      ll = computeFloat();
-      mm = computeFloat();
-      nn = computeFloat();
+      aa = $noinline$computeFloat();
+      bb = $noinline$computeFloat();
+      cc = $noinline$computeFloat();
+      dd = $noinline$computeFloat();
+      ee = $noinline$computeFloat();
+      ff = $noinline$computeFloat();
+      gg = $noinline$computeFloat();
+      hh = $noinline$computeFloat();
+      ii = $noinline$computeFloat();
+      jj = $noinline$computeFloat();
+      kk = $noinline$computeFloat();
+      ll = $noinline$computeFloat();
+      mm = $noinline$computeFloat();
+      nn = $noinline$computeFloat();
     }
   }
 
   static boolean doThrow = false;
 
-  public static double computeDouble() {
+  public static double $noinline$computeDouble() {
     if (doThrow) {
       // Try defeating inlining.
       throw new Error();
@@ -86,7 +86,7 @@
     return 2.0;
   }
 
-  public static float computeFloat() {
+  public static float $noinline$computeFloat() {
     if (doThrow) {
       // Try defeating inlining.
       throw new Error();
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index 51be912..e6aab63 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -26,7 +26,7 @@
   /// CHECK-START: void Main.invokeStaticInlined() builder (after)
   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   /// CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
-  /// CHECK-DAG:                           InvokeStaticOrDirect [<<ClinitCheck>>]
+  /// CHECK-DAG:                           InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
 
   /// CHECK-START: void Main.invokeStaticInlined() inliner (after)
   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
@@ -69,12 +69,12 @@
   /// CHECK-START: void Main.invokeStaticNotInlined() builder (after)
   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   /// CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
-  /// CHECK-DAG:                           InvokeStaticOrDirect [<<ClinitCheck>>]
+  /// CHECK-DAG:                           InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
 
   /// CHECK-START: void Main.invokeStaticNotInlined() inliner (after)
   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   /// CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
-  /// CHECK-DAG:                           InvokeStaticOrDirect [<<ClinitCheck>>]
+  /// CHECK-DAG:                           InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
 
   // The following checks ensure the clinit check and load class
   // instructions added by the builder are pruned by the
@@ -90,7 +90,7 @@
   /// CHECK-NOT:                           ClinitCheck
 
   static void invokeStaticNotInlined() {
-    ClassWithClinit2.staticMethod();
+    ClassWithClinit2.$noinline$staticMethod();
   }
 
   static class ClassWithClinit2 {
@@ -100,7 +100,7 @@
 
     static boolean doThrow = false;
 
-    static void staticMethod() {
+    static void $noinline$staticMethod() {
       if (doThrow) {
         // Try defeating inlining.
         throw new Error();
@@ -169,7 +169,7 @@
       // initialization of ClassWithClinit4, meaning that the
       // call to staticMethod below does not need a clinit
       // check.
-      staticMethod();
+      $noinline$staticMethod();
     }
 
     static {
@@ -178,7 +178,7 @@
 
     static boolean doThrow = false;
 
-    static void staticMethod() {
+    static void $noinline$staticMethod() {
       if (doThrow) {
         // Try defeating inlining.
         throw new Error();
@@ -242,7 +242,7 @@
   static class ClassWithClinit6 {
     static boolean doThrow = false;
 
-    static void staticMethod() {
+    static void $noinline$staticMethod() {
       if (doThrow) {
         // Try defeating inlining.
         throw new Error();
@@ -256,7 +256,7 @@
 
   static class SubClassOfClassWithClinit6 extends ClassWithClinit6 {
     static void invokeStaticNotInlined() {
-      ClassWithClinit6.staticMethod();
+      ClassWithClinit6.$noinline$staticMethod();
     }
   }
 
@@ -276,7 +276,7 @@
   /// CHECK-NOT:                           ClinitCheck
 
   static void noClinitBecauseOfInvokeStatic() {
-    ClassWithClinit2.staticMethod();
+    ClassWithClinit2.$noinline$staticMethod();
     ClassWithClinit2.doThrow = false;
   }
 
@@ -295,7 +295,7 @@
   /// CHECK-NOT:                           ClinitCheck
   static void clinitBecauseOfFieldAccess() {
     ClassWithClinit2.doThrow = false;
-    ClassWithClinit2.staticMethod();
+    ClassWithClinit2.$noinline$staticMethod();
   }
 
   // TODO: Add a test for the case of a static method whose declaring
diff --git a/test/486-checker-must-do-null-check/src/Main.java b/test/486-checker-must-do-null-check/src/Main.java
index ea72718..e8ff6a4 100644
--- a/test/486-checker-must-do-null-check/src/Main.java
+++ b/test/486-checker-must-do-null-check/src/Main.java
@@ -36,16 +36,16 @@
   /// CHECK:       CheckCast must_do_null_check:false
   public void CheckCastPreChecked(Object o) {
     o.toString();
-    ((Main)o).Bar();
+    ((Main)o).$noinline$Bar();
   }
 
   /// CHECK-START: void Main.CheckCast(java.lang.Object) instruction_simplifier (after)
   /// CHECK:       CheckCast must_do_null_check:true
   public void CheckCast(Object o) {
-    ((Main)o).Bar();
+    ((Main)o).$noinline$Bar();
   }
 
-  void Bar() {throw new RuntimeException();}
+  void $noinline$Bar() {throw new RuntimeException();}
 
   public static void main(String[] sa) {
     Main t = new Main();