Merge "Remove ExtractCodeAndPrelink and switch Portable to MCLinker" into dalvik-dev
diff --git a/src/compiler/llvm/gbc_expander.cc b/src/compiler/llvm/gbc_expander.cc
index 9de2e41..44cba4b 100644
--- a/src/compiler/llvm/gbc_expander.cc
+++ b/src/compiler/llvm/gbc_expander.cc
@@ -20,6 +20,7 @@
 #include "ir_builder.h"
 #include "mirror/abstract_method.h"
 #include "mirror/array.h"
+#include "mirror/string.h"
 #include "thread.h"
 #include "utils_llvm.h"
 #include "verifier/method_verifier.h"
@@ -154,6 +155,19 @@
                             llvm::Value* index_value,
                             JType elem_jty);
 
+  //----------------------------------------------------------------------------
+  // Invoke helper function
+  //----------------------------------------------------------------------------
+  llvm::Value* EmitInvoke(llvm::CallInst& call_inst);
+
+  //----------------------------------------------------------------------------
+  // Inlining helper functions
+  //----------------------------------------------------------------------------
+  bool EmitIntrinsic(llvm::CallInst& call_inst, llvm::Value** result);
+
+  bool EmitIntrinsicStringLengthOrIsEmpty(llvm::CallInst& call_inst,
+                                          llvm::Value** result, bool is_empty);
+
  private:
   //----------------------------------------------------------------------------
   // Expand Greenland intrinsics
@@ -751,6 +765,146 @@
   return irb_.CreateGEP(array_data_addr, index_value);
 }
 
+llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) {
+  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
+  art::InvokeType invoke_type =
+      static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0)));
+  bool is_static = (invoke_type == art::kStatic);
+  uint32_t callee_method_idx = LV2UInt(call_inst.getArgOperand(1));
+
+  // Load *this* actual parameter
+  llvm::Value* this_addr = (!is_static) ? call_inst.getArgOperand(3) : NULL;
+
+  // Compute invoke related information for compiler decision
+  int vtable_idx = -1;
+  uintptr_t direct_code = 0;
+  uintptr_t direct_method = 0;
+  bool is_fast_path = driver_->
+      ComputeInvokeInfo(callee_method_idx, dex_compilation_unit_,
+                        invoke_type, vtable_idx, direct_code, direct_method);
+
+  // Load the method object
+  llvm::Value* callee_method_object_addr = NULL;
+
+  if (!is_fast_path) {
+    callee_method_object_addr =
+        EmitCallRuntimeForCalleeMethodObjectAddr(callee_method_idx, invoke_type,
+                                                 this_addr, dex_pc, is_fast_path);
+  } else {
+    switch (invoke_type) {
+      case art::kStatic:
+      case art::kDirect:
+        if (direct_method != 0u &&
+            direct_method != static_cast<uintptr_t>(-1)) {
+          callee_method_object_addr =
+              irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_method),
+                                  irb_.getJObjectTy());
+        } else {
+          callee_method_object_addr =
+              EmitLoadSDCalleeMethodObjectAddr(callee_method_idx);
+        }
+        break;
+
+      case art::kVirtual:
+        DCHECK(vtable_idx != -1);
+        callee_method_object_addr =
+            EmitLoadVirtualCalleeMethodObjectAddr(vtable_idx, this_addr);
+        break;
+
+      case art::kSuper:
+        LOG(FATAL) << "invoke-super should be promoted to invoke-direct in "
+        "the fast path.";
+        break;
+
+      case art::kInterface:
+        callee_method_object_addr =
+            EmitCallRuntimeForCalleeMethodObjectAddr(callee_method_idx,
+                                                     invoke_type, this_addr,
+                                                     dex_pc, is_fast_path);
+        break;
+    }
+  }
+
+  // Load the actual parameter
+  std::vector<llvm::Value*> args;
+
+  args.push_back(callee_method_object_addr); // method object for callee
+
+  for (uint32_t i = 3; i < call_inst.getNumArgOperands(); ++i) {
+    args.push_back(call_inst.getArgOperand(i));
+  }
+
+  llvm::Value* code_addr;
+  llvm::Type* func_type = GetFunctionType(call_inst.getType(),
+                                          callee_method_idx, is_static);
+  if (direct_code != 0u && direct_code != static_cast<uintptr_t>(-1)) {
+    code_addr =
+        irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_code),
+                            func_type->getPointerTo());
+  } else {
+    code_addr =
+        irb_.LoadFromObjectOffset(callee_method_object_addr,
+                                  art::mirror::AbstractMethod::GetCodeOffset().Int32Value(),
+                                  func_type->getPointerTo(), kTBAARuntimeInfo);
+  }
+
+  // Invoke callee
+  EmitUpdateDexPC(dex_pc);
+  llvm::Value* retval = irb_.CreateCall(code_addr, args);
+  EmitGuard_ExceptionLandingPad(dex_pc);
+
+  return retval;
+}
+
+bool GBCExpanderPass::EmitIntrinsic(llvm::CallInst& call_inst,
+                                    llvm::Value** result) {
+  DCHECK(result != NULL);
+
+  uint32_t callee_method_idx = LV2UInt(call_inst.getArgOperand(1));
+  std::string callee_method_name(
+      PrettyMethod(callee_method_idx, *dex_compilation_unit_->GetDexFile()));
+
+  if (callee_method_name == "int java.lang.String.length()") {
+    return EmitIntrinsicStringLengthOrIsEmpty(call_inst, result,
+                                              false /* is_empty */);
+  }
+  if (callee_method_name == "boolean java.lang.String.isEmpty()") {
+    return EmitIntrinsicStringLengthOrIsEmpty(call_inst, result,
+                                              true /* is_empty */);
+  }
+
+  *result = NULL;
+  return false;
+}
+
+bool GBCExpanderPass::EmitIntrinsicStringLengthOrIsEmpty(llvm::CallInst& call_inst,
+                                                         llvm::Value** result,
+                                                         bool is_empty) {
+  art::InvokeType invoke_type =
+        static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0)));
+  DCHECK_NE(invoke_type, art::kStatic);
+  DCHECK_EQ(call_inst.getNumArgOperands(), 4U);
+
+  llvm::Value* this_object = call_inst.getArgOperand(3);
+  llvm::Value* string_count =
+      irb_.LoadFromObjectOffset(this_object,
+                                art::mirror::String::CountOffset().Int32Value(),
+                                irb_.getJIntTy(),
+                                kTBAAConstJObject);
+  if (is_empty) {
+    llvm::Value* count_equals_zero = irb_.CreateICmpEQ(string_count,
+                                                       irb_.getJInt(0));
+    llvm::Value* is_empty = irb_.CreateSelect(count_equals_zero,
+                                              irb_.getJBoolean(true),
+                                              irb_.getJBoolean(false));
+    is_empty = SignOrZeroExtendCat1Types(is_empty, kBoolean);
+    *result = is_empty;
+  } else {
+    *result = string_count;
+  }
+  return true;
+}
+
 void GBCExpanderPass::Expand_TestSuspend(llvm::CallInst& call_inst) {
   uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
 
@@ -1431,10 +1585,12 @@
     llvm::Value* field_addr =
       irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
 
-    // TODO: Check is_volatile.  We need to generate atomic load instruction
-    // when is_volatile is true.
     field_value = irb_.CreateLoad(field_addr, kTBAAHeapInstance, field_jty);
     field_value = SignOrZeroExtendCat1Types(field_value, field_jty);
+
+    if (is_volatile) {
+      irb_.CreateMemoryBarrier(art::kLoadLoad);
+    }
   }
 
   return field_value;
@@ -1486,6 +1642,10 @@
   } else {
     DCHECK_GE(field_offset, 0);
 
+    if (is_volatile) {
+      irb_.CreateMemoryBarrier(art::kStoreStore);
+    }
+
     llvm::PointerType* field_type =
       irb_.getJType(field_jty)->getPointerTo();
 
@@ -1494,11 +1654,13 @@
     llvm::Value* field_addr =
       irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
 
-    // TODO: Check is_volatile.  We need to generate atomic store instruction
-    // when is_volatile is true.
     new_value = TruncateCat1Types(new_value, field_jty);
     irb_.CreateStore(new_value, field_addr, kTBAAHeapInstance, field_jty);
 
+    if (is_volatile) {
+      irb_.CreateMemoryBarrier(art::kLoadLoad);
+    }
+
     if (field_jty == kObject) { // If put an object, mark the GC card table.
       EmitMarkGCCard(new_value, object_addr);
     }
@@ -1710,10 +1872,12 @@
       irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
                          irb_.getJType(field_jty)->getPointerTo());
 
-    // TODO: Check is_volatile.  We need to generate atomic load instruction
-    // when is_volatile is true.
     static_field_value = irb_.CreateLoad(static_field_addr, kTBAAHeapStatic, field_jty);
     static_field_value = SignOrZeroExtendCat1Types(static_field_value, field_jty);
+
+    if (is_volatile) {
+      irb_.CreateMemoryBarrier(art::kLoadLoad);
+    }
   }
 
   return static_field_value;
@@ -1787,17 +1951,23 @@
       static_storage_addr = EmitLoadStaticStorage(dex_pc, ssb_index);
     }
 
+    if (is_volatile) {
+      irb_.CreateMemoryBarrier(art::kStoreStore);
+    }
+
     llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset);
 
     llvm::Value* static_field_addr =
       irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
                          irb_.getJType(field_jty)->getPointerTo());
 
-    // TODO: Check is_volatile.  We need to generate atomic store instruction
-    // when is_volatile is true.
     new_value = TruncateCat1Types(new_value, field_jty);
     irb_.CreateStore(new_value, static_field_addr, kTBAAHeapStatic, field_jty);
 
+    if (is_volatile) {
+      irb_.CreateMemoryBarrier(art::kStoreLoad);
+    }
+
     if (field_jty == kObject) { // If put an object, mark the GC card table.
       EmitMarkGCCard(new_value, static_storage_addr);
     }
@@ -2062,109 +2232,24 @@
 }
 
 llvm::Value* GBCExpanderPass::Expand_HLInvoke(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
   art::InvokeType invoke_type = static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0)));
   bool is_static = (invoke_type == art::kStatic);
-  uint32_t callee_method_idx = LV2UInt(call_inst.getArgOperand(1));
-  int opt_flags = LV2UInt(call_inst.getArgOperand(2));
-
-  // Compute invoke related information for compiler decision
-  int vtable_idx = -1;
-  uintptr_t direct_code = 0;
-  uintptr_t direct_method = 0;
-  bool is_fast_path = driver_->
-    ComputeInvokeInfo(callee_method_idx, dex_compilation_unit_,
-                      invoke_type, vtable_idx, direct_code, direct_method);
-
-  // Load *this* actual parameter
-  llvm::Value* this_addr = NULL;
 
   if (!is_static) {
     // Test: Is *this* parameter equal to null?
-    this_addr = call_inst.getArgOperand(3);
+    uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
+    llvm::Value* this_addr = call_inst.getArgOperand(3);
+    int opt_flags = LV2UInt(call_inst.getArgOperand(2));
+
+    EmitGuard_NullPointerException(dex_pc, this_addr, opt_flags);
   }
 
-  // Load the method object
-  llvm::Value* callee_method_object_addr = NULL;
-
-  if (!is_fast_path) {
-    callee_method_object_addr =
-      EmitCallRuntimeForCalleeMethodObjectAddr(callee_method_idx, invoke_type,
-                                               this_addr, dex_pc, is_fast_path);
-
-    if (!is_static) {
-      EmitGuard_NullPointerException(dex_pc, this_addr, opt_flags);
-    }
-  } else {
-    if (!is_static) {
-      EmitGuard_NullPointerException(dex_pc, this_addr, opt_flags);
-    }
-
-    switch (invoke_type) {
-    case art::kStatic:
-    case art::kDirect:
-      if (direct_method != 0u &&
-          direct_method != static_cast<uintptr_t>(-1)) {
-        callee_method_object_addr =
-          irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_method),
-                              irb_.getJObjectTy());
-      } else {
-        callee_method_object_addr =
-          EmitLoadSDCalleeMethodObjectAddr(callee_method_idx);
-      }
-      break;
-
-    case art::kVirtual:
-      DCHECK(vtable_idx != -1);
-      callee_method_object_addr =
-        EmitLoadVirtualCalleeMethodObjectAddr(vtable_idx, this_addr);
-      break;
-
-    case art::kSuper:
-      LOG(FATAL) << "invoke-super should be promoted to invoke-direct in "
-                    "the fast path.";
-      break;
-
-    case art::kInterface:
-      callee_method_object_addr =
-        EmitCallRuntimeForCalleeMethodObjectAddr(callee_method_idx,
-                                                 invoke_type, this_addr,
-                                                 dex_pc, is_fast_path);
-      break;
-    }
+  llvm::Value* result = NULL;
+  if (EmitIntrinsic(call_inst, &result)) {
+    return result;
   }
 
-  // Load the actual parameter
-  std::vector<llvm::Value*> args;
-
-  args.push_back(callee_method_object_addr); // method object for callee
-
-  for (uint32_t i = 3; i < call_inst.getNumArgOperands(); ++i) {
-    args.push_back(call_inst.getArgOperand(i));
-  }
-
-  // Generate the load of the Method*. We base the return type on that of the call as method's
-  // returning a value are void calls if the return value is unused.
-  llvm::Value* code_addr;
-  if (direct_code != 0u &&
-      direct_code != static_cast<uintptr_t>(-1)) {
-    code_addr =
-      irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_code),
-                          GetFunctionType(call_inst.getType(), callee_method_idx, is_static)->getPointerTo());
-  } else {
-    code_addr =
-      irb_.LoadFromObjectOffset(callee_method_object_addr,
-                                art::mirror::AbstractMethod::GetCodeOffset().Int32Value(),
-                                GetFunctionType(call_inst.getType(), callee_method_idx, is_static)->getPointerTo(),
-                                kTBAARuntimeInfo);
-  }
-
-  // Invoke callee
-  EmitUpdateDexPC(dex_pc);
-  llvm::Value* retval = irb_.CreateCall(code_addr, args);
-  EmitGuard_ExceptionLandingPad(dex_pc);
-
-  return retval;
+  return EmitInvoke(call_inst);
 }
 
 llvm::Value* GBCExpanderPass::Expand_OptArrayLength(llvm::CallInst& call_inst) {
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index bae4d4f..f4d2dd1 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -26,6 +26,10 @@
     test_Math_abs_J();
     test_Math_min();
     test_Math_max();
+    test_StrictMath_abs_I();
+    test_StrictMath_abs_J();
+    test_StrictMath_min();
+    test_StrictMath_max();
     test_String_charAt();
     test_String_compareTo();
     test_String_indexOf();
@@ -223,6 +227,7 @@
     Assert.assertEquals(Math.abs(Integer.MAX_VALUE), Integer.MAX_VALUE);
     Assert.assertEquals(Math.abs(Integer.MIN_VALUE), Integer.MIN_VALUE);
     Assert.assertEquals(Math.abs(Integer.MIN_VALUE - 1), Integer.MAX_VALUE);
+    Assert.assertEquals(Math.abs(Integer.MIN_VALUE + 1), Integer.MAX_VALUE);
   }
 
   public static void test_Math_abs_J() {
@@ -252,6 +257,43 @@
     Assert.assertEquals(Math.max(Integer.MIN_VALUE, Integer.MAX_VALUE), Integer.MAX_VALUE);
   }
 
+  public static void test_StrictMath_abs_I() {
+    Assert.assertEquals(StrictMath.abs(0), 0);
+    Assert.assertEquals(StrictMath.abs(123), 123);
+    Assert.assertEquals(StrictMath.abs(-123), 123);
+    Assert.assertEquals(StrictMath.abs(Integer.MAX_VALUE), Integer.MAX_VALUE);
+    Assert.assertEquals(StrictMath.abs(Integer.MIN_VALUE), Integer.MIN_VALUE);
+    Assert.assertEquals(StrictMath.abs(Integer.MIN_VALUE - 1), Integer.MAX_VALUE);
+    Assert.assertEquals(StrictMath.abs(Integer.MIN_VALUE + 1), Integer.MAX_VALUE);
+  }
+
+  public static void test_StrictMath_abs_J() {
+    Assert.assertEquals(StrictMath.abs(0L), 0L);
+    Assert.assertEquals(StrictMath.abs(123L), 123L);
+    Assert.assertEquals(StrictMath.abs(-123L), 123L);
+    Assert.assertEquals(StrictMath.abs(Long.MAX_VALUE), Long.MAX_VALUE);
+    Assert.assertEquals(StrictMath.abs(Long.MIN_VALUE), Long.MIN_VALUE);
+    Assert.assertEquals(StrictMath.abs(Long.MIN_VALUE - 1), Long.MAX_VALUE);
+  }
+
+  public static void test_StrictMath_min() {
+    Assert.assertEquals(StrictMath.min(0, 0), 0);
+    Assert.assertEquals(StrictMath.min(1, 0), 0);
+    Assert.assertEquals(StrictMath.min(0, 1), 0);
+    Assert.assertEquals(StrictMath.min(0, Integer.MAX_VALUE), 0);
+    Assert.assertEquals(StrictMath.min(Integer.MIN_VALUE, 0), Integer.MIN_VALUE);
+    Assert.assertEquals(StrictMath.min(Integer.MIN_VALUE, Integer.MAX_VALUE), Integer.MIN_VALUE);
+  }
+
+  public static void test_StrictMath_max() {
+    Assert.assertEquals(StrictMath.max(0, 0), 0);
+    Assert.assertEquals(StrictMath.max(1, 0), 1);
+    Assert.assertEquals(StrictMath.max(0, 1), 1);
+    Assert.assertEquals(StrictMath.max(0, Integer.MAX_VALUE), Integer.MAX_VALUE);
+    Assert.assertEquals(StrictMath.max(Integer.MIN_VALUE, 0), 0);
+    Assert.assertEquals(StrictMath.max(Integer.MIN_VALUE, Integer.MAX_VALUE), Integer.MAX_VALUE);
+  }
+
   public static void test_Float_floatToRawIntBits() {
     Assert.assertEquals(Float.floatToRawIntBits(-1.0f), 0xbf800000);
     Assert.assertEquals(Float.floatToRawIntBits(0.0f), 0);