JIT: Support for Dalvik volatiles (issue 2781881)

Also, on SMP systems generate memory barriers.

Change-Id: If64f7c98a8de426930b8f36ac77913e53b7b2d7a
diff --git a/vm/compiler/codegen/CompilerCodegen.h b/vm/compiler/codegen/CompilerCodegen.h
index 06fd410..7379d50 100644
--- a/vm/compiler/codegen/CompilerCodegen.h
+++ b/vm/compiler/codegen/CompilerCodegen.h
@@ -61,4 +61,7 @@
 /* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
 int dvmCompilerTargetOptHint(int key);
 
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit);
+
 #endif /* _DALVIK_VM_COMPILERCODEGEN_H_ */
diff --git a/vm/compiler/codegen/arm/ArchUtility.c b/vm/compiler/codegen/arm/ArchUtility.c
index 4e0df28..2daa871 100644
--- a/vm/compiler/codegen/arm/ArchUtility.c
+++ b/vm/compiler/codegen/arm/ArchUtility.c
@@ -68,6 +68,7 @@
     char *bufEnd = &buf[size-1];
     char *fmtEnd = &fmt[strlen(fmt)];
     char tbuf[256];
+    char *name;
     char nc;
     while (fmt < fmtEnd) {
         int operand;
@@ -82,6 +83,32 @@
                assert((unsigned)(nc-'0') < 4);
                operand = lir->operands[nc-'0'];
                switch(*fmt++) {
+                   case 'B':
+                       switch (operand) {
+                           case kSY:
+                               name = "sy";
+                               break;
+                           case kST:
+                               name = "st";
+                               break;
+                           case kISH:
+                               name = "ish";
+                               break;
+                           case kISHST:
+                               name = "ishst";
+                               break;
+                           case kNSH:
+                               name = "nsh";
+                               break;
+                           case kNSHST:
+                               name = "shst";
+                               break;
+                           default:
+                               name = "DecodeError";
+                               break;
+                       }
+                       strcpy(tbuf, name);
+                       break;
                    case 'b':
                        strcpy(tbuf,"0000");
                        for (i=3; i>= 0; i--) {
diff --git a/vm/compiler/codegen/arm/ArmLIR.h b/vm/compiler/codegen/arm/ArmLIR.h
index c397a39..24f5240 100644
--- a/vm/compiler/codegen/arm/ArmLIR.h
+++ b/vm/compiler/codegen/arm/ArmLIR.h
@@ -622,10 +622,21 @@
                                   rd[11-8] imm2[7-6] [0] msb[4-0] */
     kThumb2Bfc,          /* bfc [11110011011011110] [0] imm3[14-12]
                                   rd[11-8] imm2[7-6] [0] msb[4-0] */
+    kThumb2Dmb,          /* dmb [1111001110111111100011110101] option[3-0] */
 
     kArmLast,
 } ArmOpCode;
 
+/* DMB option encodings */
+typedef enum ArmOpDmbOptions {
+    kSY = 0xf,
+    kST = 0xe,
+    kISH = 0xb,
+    kISHST = 0xa,
+    kNSH = 0x7,
+    kNSHST = 0x6
+} ArmOpDmbOptions;
+
 /* Bit flags describing the behavior of each native opcode */
 typedef enum ArmOpFeatureFlags {
     kIsBranch = 0,
diff --git a/vm/compiler/codegen/arm/Assemble.c b/vm/compiler/codegen/arm/Assemble.c
index 05c4a0e..832ee0f 100644
--- a/vm/compiler/codegen/arm/Assemble.c
+++ b/vm/compiler/codegen/arm/Assemble.c
@@ -70,6 +70,7 @@
  *     n -> complimented Thumb2 modified immediate
  *     M -> Thumb2 16-bit zero-extended immediate
  *     b -> 4-digit binary
+ *     B -> dmb option string (sy, st, ish, ishst, nsh, hshst)
  *
  *  [!] escape.  To insert "!", use "!!"
  */
@@ -869,6 +870,10 @@
                  kFmtBitBlt, 11, 8, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0,
                  "bfc", "r!0d,#!1d,#!2d", 2),
+    ENCODING_MAP(kThumb2Dmb,         0xf3bf8f50,
+                 kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP,
+                 "dmb","#!0B",2),
 };
 
 /*
diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c
index d69991d..0bcdd0e 100644
--- a/vm/compiler/codegen/arm/CodegenDriver.c
+++ b/vm/compiler/codegen/arm/CodegenDriver.c
@@ -77,7 +77,6 @@
     return false;
 }
 
-
 static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
                                     RegLocation rlDest, RegLocation rlSrc1,
                                     RegLocation rlSrc2)
@@ -301,7 +300,7 @@
  *
  */
 static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
-                    int fieldOffset)
+                    int fieldOffset, bool isVolatile)
 {
     RegLocation rlResult;
     RegisterClass regClass = dvmCompilerRegClassBySize(size);
@@ -316,6 +315,9 @@
     loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
                  size, rlObj.sRegLow);
     HEAP_ACCESS_SHADOW(false);
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit);
+    }
 
     storeValue(cUnit, rlDest, rlResult);
 }
@@ -325,7 +327,7 @@
  *
  */
 static void genIPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
-                    int fieldOffset, bool isObject)
+                    int fieldOffset, bool isObject, bool isVolatile)
 {
     RegisterClass regClass = dvmCompilerRegClassBySize(size);
     RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
@@ -335,6 +337,9 @@
     genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
                  NULL);/* null object? */
 
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit);
+    }
     HEAP_ACCESS_SHADOW(true);
     storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
     HEAP_ACCESS_SHADOW(false);
@@ -1444,6 +1449,8 @@
             storeValue(cUnit, rlDest, rlResult);
             break;
         }
+        case OP_SGET_VOLATILE:
+        case OP_SGET_OBJECT_VOLATILE:
         case OP_SGET_OBJECT:
         case OP_SGET_BOOLEAN:
         case OP_SGET_CHAR:
@@ -1452,6 +1459,7 @@
         case OP_SGET: {
             int valOffset = offsetof(StaticField, value);
             int tReg = dvmCompilerAllocTemp(cUnit);
+            bool isVolatile;
             void *fieldPtr = (void*)
               (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
 
@@ -1460,10 +1468,17 @@
                 dvmAbort();
             }
 
+            isVolatile = (mir->dalvikInsn.opCode == OP_SGET_VOLATILE) ||
+                         (mir->dalvikInsn.opCode == OP_SGET_OBJECT_VOLATILE) ||
+                         dvmIsVolatileField(fieldPtr);
+
             rlDest = dvmCompilerGetDest(cUnit, mir, 0);
             rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
             loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
 
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit);
+            }
             HEAP_ACCESS_SHADOW(true);
             loadWordDisp(cUnit, tReg, 0, rlResult.lowReg);
             HEAP_ACCESS_SHADOW(false);
@@ -1501,9 +1516,14 @@
         case OP_SPUT: {
             int valOffset = offsetof(StaticField, value);
             int tReg = dvmCompilerAllocTemp(cUnit);
-            void *fieldPtr = (void*)
+            bool isVolatile;
+            Field *fieldPtr =
               (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
 
+            isVolatile = (mir->dalvikInsn.opCode == OP_SPUT_VOLATILE) ||
+                         (mir->dalvikInsn.opCode == OP_SPUT_OBJECT_VOLATILE) ||
+                         dvmIsVolatileField(fieldPtr);
+
             if (fieldPtr == NULL) {
                 LOGE("Unexpected null static field");
                 dvmAbort();
@@ -1516,6 +1536,9 @@
             HEAP_ACCESS_SHADOW(true);
             storeWordDisp(cUnit, tReg, 0 ,rlSrc.lowReg);
             HEAP_ACCESS_SHADOW(false);
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit);
+            }
             if (mir->dalvikInsn.opCode == OP_SPUT_OBJECT) {
                 /* NOTE: marking card based on field address */
                 markCard(cUnit, rlSrc.lowReg, tReg);
@@ -2120,17 +2143,18 @@
 {
     OpCode dalvikOpCode = mir->dalvikInsn.opCode;
     int fieldOffset;
+    bool isVolatile = false;
 
     if (dalvikOpCode >= OP_IGET && dalvikOpCode <= OP_IPUT_SHORT) {
-        InstField *pInstField = (InstField *)
+        Field *fieldPtr =
             cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC];
 
-        if (pInstField == NULL) {
+        if (fieldPtr == NULL) {
             LOGE("Unexpected null instance field");
             dvmAbort();
         }
-
-        fieldOffset = pInstField->byteOffset;
+        isVolatile = dvmIsVolatileField(fieldPtr);
+        fieldOffset = ((InstField *)fieldPtr)->byteOffset;
     } else {
         /* Deliberately break the code while make the compiler happy */
         fieldOffset = -1;
@@ -2230,38 +2254,47 @@
         case OP_IGET_WIDE:
             genIGetWide(cUnit, mir, fieldOffset);
             break;
+        case OP_IGET_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+            isVolatile = true;
+            // NOTE: intentional fallthrough
         case OP_IGET:
         case OP_IGET_OBJECT:
-            genIGet(cUnit, mir, kWord, fieldOffset);
+            genIGet(cUnit, mir, kWord, fieldOffset, isVolatile);
             break;
         case OP_IGET_BOOLEAN:
-            genIGet(cUnit, mir, kUnsignedByte, fieldOffset);
+            genIGet(cUnit, mir, kUnsignedByte, fieldOffset, isVolatile);
             break;
         case OP_IGET_BYTE:
-            genIGet(cUnit, mir, kSignedByte, fieldOffset);
+            genIGet(cUnit, mir, kSignedByte, fieldOffset, isVolatile);
             break;
         case OP_IGET_CHAR:
-            genIGet(cUnit, mir, kUnsignedHalf, fieldOffset);
+            genIGet(cUnit, mir, kUnsignedHalf, fieldOffset, isVolatile);
             break;
         case OP_IGET_SHORT:
-            genIGet(cUnit, mir, kSignedHalf, fieldOffset);
+            genIGet(cUnit, mir, kSignedHalf, fieldOffset, isVolatile);
             break;
         case OP_IPUT_WIDE:
             genIPutWide(cUnit, mir, fieldOffset);
             break;
         case OP_IPUT:
-            genIPut(cUnit, mir, kWord, fieldOffset, false);
+            genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile);
             break;
+        case OP_IPUT_OBJECT_VOLATILE:
+            isVolatile = true;
+            // NOTE: intentional fallthrough
         case OP_IPUT_OBJECT:
-            genIPut(cUnit, mir, kWord, fieldOffset, true);
+            genIPut(cUnit, mir, kWord, fieldOffset, true, isVolatile);
             break;
         case OP_IPUT_SHORT:
         case OP_IPUT_CHAR:
-            genIPut(cUnit, mir, kUnsignedHalf, fieldOffset, false);
+            genIPut(cUnit, mir, kUnsignedHalf, fieldOffset, false, isVolatile);
             break;
         case OP_IPUT_BYTE:
+            genIPut(cUnit, mir, kSignedByte, fieldOffset, false, isVolatile);
+            break;
         case OP_IPUT_BOOLEAN:
-            genIPut(cUnit, mir, kUnsignedByte, fieldOffset, false);
+            genIPut(cUnit, mir, kUnsignedByte, fieldOffset, false, isVolatile);
             break;
         case OP_IGET_WIDE_VOLATILE:
         case OP_IPUT_WIDE_VOLATILE:
@@ -2282,13 +2315,13 @@
     switch (dalvikOpCode) {
         case OP_IGET_QUICK:
         case OP_IGET_OBJECT_QUICK:
-            genIGet(cUnit, mir, kWord, fieldOffset);
+            genIGet(cUnit, mir, kWord, fieldOffset, false);
             break;
         case OP_IPUT_QUICK:
-            genIPut(cUnit, mir, kWord, fieldOffset, false);
+            genIPut(cUnit, mir, kWord, fieldOffset, false, false);
             break;
         case OP_IPUT_OBJECT_QUICK:
-            genIPut(cUnit, mir, kWord, fieldOffset, true);
+            genIPut(cUnit, mir, kWord, fieldOffset, true, false);
             break;
         case OP_IGET_WIDE_QUICK:
             genIGetWide(cUnit, mir, fieldOffset);
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c
index 7f9fa3b..6511eac 100644
--- a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c
+++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c
@@ -92,3 +92,10 @@
     }
     return res;
 }
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+#error armv5+smp not supported
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.c b/vm/compiler/codegen/arm/armv5te/ArchVariant.c
index e018ea1..814f410 100644
--- a/vm/compiler/codegen/arm/armv5te/ArchVariant.c
+++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.c
@@ -92,3 +92,10 @@
     }
     return res;
 }
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+#error armv5+smp not supported
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c
index 5a14774..f1727c6 100644
--- a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c
+++ b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c
@@ -87,3 +87,11 @@
     }
     return res;
 }
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+    ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, kSY);  // Full system DMB
+    dmb->defMask = ENCODE_ALL;
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.c b/vm/compiler/codegen/arm/armv7-a/ArchVariant.c
index 5a14774..f1727c6 100644
--- a/vm/compiler/codegen/arm/armv7-a/ArchVariant.c
+++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.c
@@ -87,3 +87,11 @@
     }
     return res;
 }
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+    ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, kSY);  // Full system DMB
+    dmb->defMask = ENCODE_ALL;
+#endif
+}