Merge "Revert "Add regression tests for 32-bit x86 struct layout bug fixes.""
diff --git a/Android.mk b/Android.mk
index 6b53d4e..319f855 100644
--- a/Android.mk
+++ b/Android.mk
@@ -22,6 +22,10 @@
     host_cross_bcc_compat
 endif
 
+# Not building RenderScript modules in PDK builds, as libmediandk
+# is not available in PDK.
+ifneq ($(TARGET_BUILD_PDK), true)
+
 rs_base_CFLAGS := -Werror -Wall -Wextra \
 	-Wno-unused-parameter -Wno-unused-variable
 
@@ -229,7 +233,7 @@
 # Treble configuration
 LOCAL_SHARED_LIBRARIES += libhidlbase libhidltransport libhwbinder libutils android.hardware.renderscript@1.0
 
-LOCAL_SHARED_LIBRARIES += liblog libcutils
+LOCAL_SHARED_LIBRARIES += liblog libcutils libandroid_runtime
 
 LOCAL_STATIC_LIBRARIES := \
         libRSDispatch
@@ -240,4 +244,7 @@
 
 include $(BUILD_SHARED_LIBRARY)
 
+endif # TARGET_BUILD_PDK
+
 include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/cpu_ref/Android.mk b/cpu_ref/Android.mk
index d50a77e..9b3e9d3 100644
--- a/cpu_ref/Android.mk
+++ b/cpu_ref/Android.mk
@@ -1,11 +1,12 @@
 LOCAL_PATH:=$(call my-dir)
 
+# Not building RenderScript modules in PDK builds, as libmediandk
+# is not available in PDK.
+ifneq ($(TARGET_BUILD_PDK), true)
+
 rs_base_CFLAGS := -Werror -Wall -Wextra \
 				  -Wno-unused-parameter -Wno-unused-variable \
 				  -std=c++11
-ifeq ($(TARGET_BUILD_PDK), true)
-  rs_base_CFLAGS += -D__RS_PDK__
-endif
 
 ifneq ($(OVERRIDE_RS_DRIVER),)
   rs_base_CFLAGS += -DOVERRIDE_RS_DRIVER=$(OVERRIDE_RS_DRIVER)
@@ -98,3 +99,5 @@
 LOCAL_CFLAGS += $(rs_base_CFLAGS)
 
 include $(BUILD_SHARED_LIBRARY)
+
+endif # TARGET_BUILD_PDK
diff --git a/cpu_ref/rsCpuScriptGroup.cpp b/cpu_ref/rsCpuScriptGroup.cpp
index cc2933c..6a46427 100644
--- a/cpu_ref/rsCpuScriptGroup.cpp
+++ b/cpu_ref/rsCpuScriptGroup.cpp
@@ -90,8 +90,6 @@
 
         uint32_t ostep;
         if (sl->outs[ct]) {
-            rsAssert(kinfo->outLen == 1);
-
             mkinfo->outPtr[0] =
               (uint8_t *)sl->outs[ct]->mHal.drvState.lod[0].mallocPtr;
 
@@ -108,8 +106,6 @@
                   sl->outs[ct]->mHal.drvState.lod[0].stride * kinfo->lid;
             }
         } else {
-            rsAssert(kinfo->outLen == 0);
-
             mkinfo->outPtr[0] = nullptr;
             ostep             = 0;
         }
diff --git a/cpu_ref/rsCpuScriptGroup2.cpp b/cpu_ref/rsCpuScriptGroup2.cpp
index a8b72c4..e42efe1 100644
--- a/cpu_ref/rsCpuScriptGroup2.cpp
+++ b/cpu_ref/rsCpuScriptGroup2.cpp
@@ -72,7 +72,6 @@
             ptr += out->mHal.drvState.lod[0].stride * kinfo->current.y;
         }
 
-        rsAssert(kinfo->outLen <= 1);
         mutable_kinfo->outPtr[0] = const_cast<uint8_t*>(ptr);
 
         // The implementation of an intrinsic relies on kinfo->usr being
diff --git a/driver/rsdAllocation.cpp b/driver/rsdAllocation.cpp
index f4674b9..2db5633 100644
--- a/driver/rsdAllocation.cpp
+++ b/driver/rsdAllocation.cpp
@@ -23,10 +23,6 @@
 #include "rsCompatibilityLib.h"
 #else
 #include "rsdFrameBufferObj.h"
-#include "gui/GLConsumer.h"
-#include "gui/CpuConsumer.h"
-#include "gui/Surface.h"
-#include "hardware/gralloc.h"
 
 #include <GLES/gl.h>
 #include <GLES2/gl2.h>
@@ -99,7 +95,7 @@
 static void Update2DTexture(const Context *rsc, const Allocation *alloc, const void *ptr,
                             uint32_t xoff, uint32_t yoff, uint32_t lod,
                             RsAllocationCubemapFace face, uint32_t w, uint32_t h) {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
     rsAssert(drv->textureID);
@@ -114,7 +110,7 @@
 }
 
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 static void Upload2DTexture(const Context *rsc, const Allocation *alloc, bool isFirstUpload) {
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
@@ -158,7 +154,7 @@
 #endif
 
 static void UploadToTexture(const Context *rsc, const Allocation *alloc) {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
     if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_INPUT) {
@@ -196,7 +192,7 @@
 }
 
 static void AllocateRenderTarget(const Context *rsc, const Allocation *alloc) {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
     if (!drv->glFormat) {
@@ -221,7 +217,7 @@
 }
 
 static void UploadToBufferObject(const Context *rsc, const Allocation *alloc) {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
     rsAssert(!alloc->mHal.state.type->getDimY());
@@ -252,7 +248,7 @@
     // For the flexible YCbCr format, layout is initialized during call to
     // Allocation::ioReceive.  Return early and avoid clobberring any
     // pre-existing layout.
-    if (yuv == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+    if (yuv == RS_YUV_420_888) {
         return 0;
     }
 #endif
@@ -269,7 +265,7 @@
     state->lodCount = 3;
 
     switch(yuv) {
-    case HAL_PIXEL_FORMAT_YV12:
+    case RS_YUV_YV12:
         state->lod[2].stride = rsRound(state->lod[0].stride >> 1, 16);
         state->lod[2].mallocPtr = ((uint8_t *)state->lod[0].mallocPtr) +
                 (state->lod[0].stride * state->lod[0].dimY);
@@ -280,7 +276,7 @@
                 (state->lod[2].stride * state->lod[2].dimY);
         uvSize += state->lod[1].stride * state->lod[2].dimY;
         break;
-    case HAL_PIXEL_FORMAT_YCrCb_420_SP:  // NV21
+    case RS_YUV_NV21:
         //state->lod[1].dimX = state->lod[0].dimX;
         state->lod[1].stride = state->lod[0].stride;
         state->lod[2].stride = state->lod[0].stride;
@@ -473,8 +469,9 @@
         drv->uploadDeferred = true;
     }
 
-
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     drv->readBackFBO = nullptr;
+#endif
 
     // fill out the initial state of the buffer if we couldn't use the user-provided ptr and USAGE_SHARED was accepted
     if ((alloc->mHal.state.userProvidedPtr != 0) && (drv->useUserProvidedPtr == false)) {
@@ -535,7 +532,7 @@
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
     if (alloc->mHal.state.baseAlloc == nullptr) {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
         if (drv->bufferID) {
             // Causes a SW crash....
             //ALOGV(" mBufferID %i", mBufferID);
@@ -563,11 +560,12 @@
         }
 
 #ifndef RS_COMPATIBILITY_LIB
+#ifndef RS_VENDOR_LIB
         if (drv->readBackFBO != nullptr) {
             delete drv->readBackFBO;
             drv->readBackFBO = nullptr;
         }
-
+#endif
         if ((alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT) &&
             (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT)) {
             ANativeWindow *nw = drv->wndSurface;
@@ -615,7 +613,7 @@
 }
 
 static void rsdAllocationSyncFromFBO(const Context *rsc, const Allocation *alloc) {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     if (!alloc->getIsScript()) {
         return; // nothing to sync
     }
@@ -754,11 +752,13 @@
 #ifndef RS_COMPATIBILITY_LIB
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
     ANativeWindow *nw = drv->wndSurface;
+#ifndef RS_VENDOR_LIB
     if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET) {
         RsdHal *dc = (RsdHal *)rsc->mHal.drv;
         RSD_CALL_GL(eglSwapBuffers, dc->gl.egl.display, dc->gl.egl.surface);
         return;
     }
+#endif
     if (nw) {
         if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT) {
             int32_t r = ANativeWindow_unlockAndPost(nw);
@@ -776,7 +776,7 @@
 }
 
 void rsdAllocationIoReceive(const Context *rsc, Allocation *alloc) {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
     if (!(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT)) {
         drv->surfaceTexture->updateTexImage();
@@ -842,10 +842,10 @@
             size_t clineSize = lineSize;
             int lod = 1;
             int maxLod = 2;
-            if (alloc->mHal.state.yuv == HAL_PIXEL_FORMAT_YV12) {
+            if (alloc->mHal.state.yuv == RS_YUV_YV12) {
                 maxLod = 3;
                 clineSize >>= 1;
-            } else if (alloc->mHal.state.yuv == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
+            } else if (alloc->mHal.state.yuv == RS_YUV_NV21) {
                 lod = 2;
                 maxLod = 3;
             }
diff --git a/driver/rsdAllocation.h b/driver/rsdAllocation.h
index c2d1467..e37299e 100644
--- a/driver/rsdAllocation.h
+++ b/driver/rsdAllocation.h
@@ -26,7 +26,7 @@
 #include <GLES/gl.h>
 #include <GLES2/gl2.h>
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 #include "gui/GLConsumer.h"
 #endif
 
@@ -48,7 +48,7 @@
     // Is this a legal structure to be used as an FBO render target
     uint32_t renderTargetID;
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     GLenum glTarget;
     GLenum glType;
     GLenum glFormat;
@@ -65,7 +65,9 @@
     bool useUserProvidedPtr;
     bool uploadDeferred;
 
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     RsdFrameBufferObj * readBackFBO;
+#endif
     ANativeWindow *wnd;
     ANativeWindow *wndSurface;
 };
diff --git a/driver/rsdCore.cpp b/driver/rsdCore.cpp
index 07ca2a6..cd48340 100644
--- a/driver/rsdCore.cpp
+++ b/driver/rsdCore.cpp
@@ -21,7 +21,7 @@
 #include "rsdBcc.h"
 #include "rsdElement.h"
 #include "rsdType.h"
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     #include "rsdGL.h"
     #include "rsdProgramStore.h"
     #include "rsdProgramRaster.h"
@@ -208,7 +208,7 @@
     // Functions below this point are for the legacy graphics api,
     // vendor drivers are NOT expected to implement these.  They will never be called
     // for an external driver.
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     case RS_HAL_GRAPHICS_INIT:
         fnPtr[0] = (void *)rsdGLInit; break;
     case RS_HAL_GRAPHICS_SHUTDOWN:
@@ -353,7 +353,7 @@
 
     dc->mCpuRef->setPriority(priority);
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     if (dc->mHasGraphics) {
         rsdGLSetPriority(rsc, priority);
     }
diff --git a/driver/rsdCore.h b/driver/rsdCore.h
index 0e46fd2..3cad348 100644
--- a/driver/rsdCore.h
+++ b/driver/rsdCore.h
@@ -24,7 +24,7 @@
 #include "rsMutex.h"
 #include "rsSignal.h"
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 #include "rsdGL.h"
 #endif
 
@@ -41,7 +41,7 @@
     ScriptTLSStruct mTlsStruct;
     android::renderscript::RsdCpuReference *mCpuRef;
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     RsdGL gl;
 #endif
 } RsdHal;
diff --git a/driver/rsdRuntimeStubs.cpp b/driver/rsdRuntimeStubs.cpp
index 498cea4..504f098 100644
--- a/driver/rsdRuntimeStubs.cpp
+++ b/driver/rsdRuntimeStubs.cpp
@@ -32,7 +32,7 @@
 
 #include <time.h>
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 using android::renderscript::Font;
 #endif
 
@@ -807,7 +807,7 @@
 //////////////////////////////////////////////////////////////////////////////
 // Graphics routines
 //////////////////////////////////////////////////////////////////////////////
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 static void SC_DrawQuadTexCoords(float x1, float y1, float z1, float u1, float v1,
                                  float x2, float y2, float z2, float u2, float v2,
                                  float x3, float y3, float z3, float u3, float v3,
diff --git a/rsAllocation.h b/rsAllocation.h
index 78be2fc..602a666 100644
--- a/rsAllocation.h
+++ b/rsAllocation.h
@@ -19,13 +19,6 @@
 
 #include "rsType.h"
 
-#ifndef RS_COMPATIBILITY_LIB
-#include "gui/CpuConsumer.h"
-#include "gui/GLConsumer.h"
-#else
-struct ANativeWindowBuffer;
-#endif
-
 #include <vector>
 
 // ---------------------------------------------------------------------------
@@ -72,7 +65,9 @@
             bool hasReferences;
             void * userProvidedPtr;
             int32_t surfaceTextureID;
-            ANativeWindowBuffer *nativeBuffer;
+            // nativeBuffer is not used anymore, keeping it here
+            // as a void* to keep the structure of Allocation the same.
+            void *nativeBuffer;
             int64_t timestamp;
 
             // Allocation adapter state
diff --git a/rsApiContext.cpp b/rsApiContext.cpp
index 9912c89..1160985 100644
--- a/rsApiContext.cpp
+++ b/rsApiContext.cpp
@@ -45,7 +45,7 @@
 // TODO: Figure out better naming schemes for all the rs* functions.
 // Currently they share the same names as the NDK counterparts, and that is
 // causing lots of confusion.
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 extern "C" RsContext rsContextCreateGL(RsDevice vdev, uint32_t version,
                                        uint32_t sdkVersion, RsSurfaceConfig sc,
                                        uint32_t dpi) {
diff --git a/rsApiStubs.cpp b/rsApiStubs.cpp
index bc08d7a..2c9a5ba 100644
--- a/rsApiStubs.cpp
+++ b/rsApiStubs.cpp
@@ -19,6 +19,11 @@
 #include "rsFallbackAdaptation.h"
 #include "cpp/rsDispatch.h"
 
+#include <android_runtime/AndroidRuntime.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RenderScript"
+
 // TODO: Figure out how to use different declared types for the two interfaces
 //       to avoid the confusion. Currently, RsContext is used as the API type for
 //       both the client interface and the dispatch table interface, but at the
@@ -65,6 +70,10 @@
 
 // Context
 
+// Mutex for locking reflection operation.
+static std::mutex reflectionMutex;
+// The defaultCacheDir will be reused if set, instead of query JNI.
+static std::string defaultCacheDir;
 extern "C" RsContext rsContextCreate(RsDevice vdev, uint32_t version, uint32_t sdkVersion,
                                      RsContextType ct, uint32_t flags)
 {
@@ -78,6 +87,44 @@
     }
 
     RsContextWrapper *ctxWrapper = new RsContextWrapper{context, instance.GetEntryFuncs()};
+
+    std::unique_lock<std::mutex> lock(reflectionMutex);
+    if (defaultCacheDir.size() == 0) {
+        // Use reflection to query the default cache dir.
+        // First check if we have JavaVM running in this process.
+        if (android::AndroidRuntime::getJavaVM()) {
+            JNIEnv* env = android::AndroidRuntime::getJNIEnv();
+            if (env) {
+                jclass cacheDirClass = env->FindClass("android/renderscript/RenderScriptCacheDir");
+                jfieldID cacheDirID = env->GetStaticFieldID(cacheDirClass, "mCacheDir", "Ljava/io/File;");
+                jobject cache_dir = env->GetStaticObjectField(cacheDirClass, cacheDirID);
+
+                if (cache_dir) {
+                    jclass fileClass = env->FindClass("java/io/File");
+                    jmethodID getPath = env->GetMethodID(fileClass, "getPath", "()Ljava/lang/String;");
+                    jstring path_string = (jstring)env->CallObjectMethod(cache_dir, getPath);
+                    const char *path_chars = env->GetStringUTFChars(path_string, NULL);
+
+                    ALOGD("Successfully queried cache dir: %s", path_chars);
+                    defaultCacheDir = std::string(path_chars);
+                    env->ReleaseStringUTFChars(path_string, path_chars);
+                } else {
+                    ALOGD("Cache dir not initialized");
+                }
+            } else {
+                ALOGD("Failed to query the default cache dir.");
+            }
+        } else {
+            ALOGD("Non JavaVM found in the process.");
+        }
+    }
+
+    if (defaultCacheDir.size() > 0) {
+        ALOGD("Setting cache dir: %s", defaultCacheDir.c_str());
+        rsContextSetCacheDir(ctxWrapper,
+                             defaultCacheDir.c_str(),
+                             defaultCacheDir.size());
+    }
     return (RsContext) ctxWrapper;
 }
 
diff --git a/rsContext.cpp b/rsContext.cpp
index 51a8ca9..da4262f 100644
--- a/rsContext.cpp
+++ b/rsContext.cpp
@@ -21,7 +21,7 @@
 
 #include "rsgApiStructs.h"
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 #include "rsMesh.h"
 #include <gui/DisplayEventReceiver.h>
 #endif
@@ -68,7 +68,7 @@
 
 Context::PushState::PushState(Context *con) {
     mRsc = con;
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     if (con->mIsGraphicsContext) {
         mFragment.set(con->getProgramFragment());
         mVertex.set(con->getProgramVertex());
@@ -80,7 +80,7 @@
 }
 
 Context::PushState::~PushState() {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     if (mRsc->mIsGraphicsContext) {
         mRsc->setProgramFragment(mFragment.get());
         mRsc->setProgramVertex(mVertex.get());
@@ -101,7 +101,7 @@
 
 uint32_t Context::runRootScript() {
     timerSet(RS_TIMER_SCRIPT);
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     mStateFragmentStore.mLast.clear();
 #endif
     watchdog.inRoot = true;
@@ -112,13 +112,9 @@
 }
 
 uint64_t Context::getTime() const {
-#ifndef ANDROID_RS_SERIALIZE
     struct timespec t;
     clock_gettime(CLOCK_MONOTONIC, &t);
     return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
-#else
-    return 0;
-#endif //ANDROID_RS_SERIALIZE
 }
 
 void Context::timerReset() {
@@ -184,7 +180,7 @@
 }
 
 bool Context::setupCheck() {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     mFragmentStore->setup(this, &mStateFragmentStore);
     mFragment->setup(this, &mStateFragment);
     mRaster->setup(this, &mStateRaster);
@@ -194,7 +190,7 @@
     return true;
 }
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 void Context::setupProgramStore() {
     mFragmentStore->setup(this, &mStateFragmentStore);
 }
@@ -211,7 +207,7 @@
 }
 
 void Context::displayDebugStats() {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     char buffer[128];
     snprintf(buffer, sizeof(buffer), "Avg fps %u, Frame %i ms, Script %i ms",
              mAverageFPS, mTimeMSLastFrame, mTimeMSLastScript);
@@ -236,10 +232,7 @@
 void * Context::threadProc(void *vrsc) {
     Context *rsc = static_cast<Context *>(vrsc);
 
-#ifndef ANDROID_RS_SERIALIZE
     rsc->mNativeThreadId = gettid();
-#endif //ANDROID_RS_SERIALIZE
-
     rsc->props.mLogTimes = getProp("debug.rs.profile") != 0;
     rsc->props.mLogScripts = getProp("debug.rs.script") != 0;
     rsc->props.mLogShaders = getProp("debug.rs.shader") != 0;
@@ -280,7 +273,7 @@
         rsc->setPriority(RS_THREAD_PRIORITY_NORMAL_GRAPHICS);
     }
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     if (rsc->mIsGraphicsContext) {
         if (!rsc->initGLThread()) {
             rsc->setError(RS_ERROR_OUT_OF_MEMORY, "Failed initializing GL");
@@ -312,12 +305,11 @@
         while (!rsc->mExit) {
             rsc->mIO.playCoreCommands(rsc, -1);
         }
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     } else {
-#ifndef ANDROID_RS_SERIALIZE
         DisplayEventReceiver displayEvent;
         DisplayEventReceiver::Event eventBuffer[1];
-#endif
+
         int vsyncRate = 0;
         int targetRate = 0;
 
@@ -325,7 +317,6 @@
         while (!rsc->mExit) {
             rsc->timerSet(RS_TIMER_IDLE);
 
-#ifndef ANDROID_RS_SERIALIZE
             if (!rsc->mRootScript.get() || !rsc->mHasSurface || rsc->mPaused) {
                 targetRate = 0;
             }
@@ -339,9 +330,7 @@
                 while (displayEvent.getEvents(eventBuffer, 1) != 0) {
                     //ALOGE("vs2 time past %lld", (rsc->getTime() - eventBuffer[0].header.timestamp) / 1000000);
                 }
-            } else
-#endif
-            {
+            } else {
                 drawOnce |= rsc->mIO.playCoreCommands(rsc, -1);
             }
 
@@ -368,7 +357,7 @@
 
     //ALOGV("%p RS Thread exiting", rsc);
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     if (rsc->mIsGraphicsContext) {
         pthread_mutex_lock(&gInitMutex);
         rsc->deinitEGL();
@@ -383,7 +372,7 @@
 void Context::destroyWorkerThreadResources() {
     //ALOGV("destroyWorkerThreadResources 1");
     ObjectBase::zeroAllUserRef(this);
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     if (mIsGraphicsContext) {
          mRaster.clear();
          mFragment.clear();
@@ -469,14 +458,12 @@
 }
 
 void Context::setCacheDir(const char * cacheDir_arg, uint32_t length) {
-    if (!hasSetCacheDir) {
-        if (length <= PATH_MAX) {
-            memcpy(mCacheDir, cacheDir_arg, length);
-            mCacheDir[length] = 0;
-            hasSetCacheDir = true;
-        } else {
-            setError(RS_ERROR_BAD_VALUE, "Invalid path");
-        }
+    if (length <= PATH_MAX) {
+        memcpy(mCacheDir, cacheDir_arg, length);
+        mCacheDir[length] = 0;
+        hasSetCacheDir = true;
+    } else {
+        setError(RS_ERROR_BAD_VALUE, "Invalid path");
     }
 }
 
@@ -583,7 +570,7 @@
     }
 }
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 void Context::setSurface(uint32_t w, uint32_t h, RsNativeWindow sur) {
     rsAssert(mIsGraphicsContext);
     mHal.funcs.setSurface(this, w, h, sur);
@@ -761,7 +748,7 @@
 }
 
 void rsi_ContextBindRootScript(Context *rsc, RsScript vs) {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     Script *s = static_cast<Script *>(vs);
     rsc->setRootScript(s);
 #endif
@@ -782,7 +769,7 @@
     s->bindToContext(&rsc->mStateSampler, slot);
 }
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 void rsi_ContextBindProgramStore(Context *rsc, RsProgramStore vpfs) {
     ProgramStore *pfs = static_cast<ProgramStore *>(vpfs);
     rsc->setProgramStore(pfs);
@@ -820,7 +807,7 @@
     ob->decUserRef();
 }
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 void rsi_ContextPause(Context *rsc) {
     rsc->pause();
 }
diff --git a/rsContext.h b/rsContext.h
index b600018..2122460 100644
--- a/rsContext.h
+++ b/rsContext.h
@@ -96,7 +96,7 @@
         ~PushState();
 
     private:
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
         ObjectBaseRef<ProgramFragment> mFragment;
         ObjectBaseRef<ProgramVertex> mVertex;
         ObjectBaseRef<ProgramStore> mStore;
@@ -115,7 +115,7 @@
     bool isSynchronous() {return mSynchronous;}
     bool setupCheck();
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     FBOCache mFBOCache;
     ProgramFragmentState mStateFragment;
     ProgramStoreState mStateFragmentStore;
@@ -162,7 +162,7 @@
     void initToClient();
     void deinitToClient();
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     ProgramFragment * getDefaultProgramFragment() const {
         return mStateFragment.mDefault.get();
     }
@@ -319,7 +319,7 @@
     pid_t mNativeThreadId;
 
     ObjectBaseRef<Script> mRootScript;
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     ObjectBaseRef<ProgramFragment> mFragment;
     ObjectBaseRef<ProgramVertex> mVertex;
     ObjectBaseRef<ProgramStore> mFragmentStore;
diff --git a/rsDebugHelper.h b/rsDebugHelper.h
index 2356e0e..82aa314 100644
--- a/rsDebugHelper.h
+++ b/rsDebugHelper.h
@@ -20,7 +20,7 @@
 #include "rsUtils.h"
 #include "rsInternalDefines.h"
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 #include <utils/CallStack.h>
 #endif
 
@@ -31,20 +31,20 @@
 class DebugHelper {
 public:
     DebugHelper() {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
         mStack.update(2);
 #endif
     }
 
     void dump() {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
         ALOGV("%s", mStack.toString().string());
         //mStack.dump();
 #endif
     }
 
 private:
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     CallStack mStack;
 #endif
 };
diff --git a/rsDriverLoader.cpp b/rsDriverLoader.cpp
index 773fcdf..48ff990 100644
--- a/rsDriverLoader.cpp
+++ b/rsDriverLoader.cpp
@@ -21,9 +21,8 @@
 
 #include "rsgApiStructs.h"
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 #include "rsMesh.h"
-#include <gui/DisplayEventReceiver.h>
 #endif
 
 #include <sys/types.h>
diff --git a/rsFont.h b/rsFont.h
index d08c6f2..956fddf 100644
--- a/rsFont.h
+++ b/rsFont.h
@@ -19,6 +19,7 @@
 
 #include "rsStream.h"
 #include <utils/KeyedVector.h>
+#include <utils/Unicode.h>
 
 struct FT_LibraryRec_;
 struct FT_FaceRec_;
diff --git a/rsScript.h b/rsScript.h
index a4cd9c8..f076c4b 100644
--- a/rsScript.h
+++ b/rsScript.h
@@ -26,7 +26,7 @@
 namespace android {
 namespace renderscript {
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
 class ProgramVertex;
 class ProgramFragment;
 class ProgramRaster;
@@ -105,7 +105,7 @@
         int64_t mStartTimeMillis;
         mutable int64_t mLastDtTime;
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
         ObjectBaseRef<ProgramVertex> mVertex;
         ObjectBaseRef<ProgramFragment> mFragment;
         ObjectBaseRef<ProgramRaster> mRaster;
diff --git a/rsScriptC.cpp b/rsScriptC.cpp
index 628d063..5566398 100644
--- a/rsScriptC.cpp
+++ b/rsScriptC.cpp
@@ -127,7 +127,7 @@
 }
 
 void ScriptC::setupGLState(Context *rsc) {
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     if (mEnviroment.mFragmentStore.get()) {
         rsc->setProgramStore(mEnviroment.mFragmentStore.get());
     }
@@ -258,7 +258,6 @@
 static const bool kDebugBitcode = false;
 
 #ifndef RS_COMPATIBILITY_LIB
-#ifndef ANDROID_RS_SERIALIZE
 
 static bool dumpBitcodeFile(const char *cacheDir, const char *resName,
                             const char *suffix, const uint8_t *bitcode,
@@ -289,7 +288,6 @@
     return true;
 }
 
-#endif  // !ANDROID_RS_SERIALIZE
 #endif  // !RS_COMPATIBILITY_LIB
 
 
@@ -301,7 +299,6 @@
     ATRACE_CALL();
     //ALOGE("runCompiler %p %p %p %p %p %i", rsc, this, resName, cacheDir, bitcode, bitcodeLen);
 #ifndef RS_COMPATIBILITY_LIB
-#ifndef ANDROID_RS_SERIALIZE
     uint32_t sdkVersion = 0;
     bcinfo::BitcodeWrapper bcWrapper((const char *)bitcode, bitcodeLen);
     if (!bcWrapper.unwrap()) {
@@ -343,7 +340,6 @@
     // optimization level used to compile the bitcode.
     rsc->setOptLevel(bcWrapper.getOptimizationLevel());
 
-#endif
     if (!cacheDir) {
         // MUST BE FIXED BEFORE ANYTHING USING C++ API IS RELEASED
         cacheDir = getenv("EXTERNAL_STORAGE");
@@ -361,7 +357,7 @@
     }
 
     mInitialized = true;
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
     mEnviroment.mFragment.set(rsc->getDefaultProgramFragment());
     mEnviroment.mVertex.set(rsc->getDefaultProgramVertex());
     mEnviroment.mFragmentStore.set(rsc->getDefaultProgramStore());
@@ -382,7 +378,7 @@
             return false;
         }
 
-#ifndef RS_COMPATIBILITY_LIB
+#if !defined(RS_VENDOR_LIB) && !defined(RS_COMPATIBILITY_LIB)
         if (!strcmp(key, "stateVertex")) {
             if (!strcmp(value, "default")) {
                 continue;
diff --git a/rsov/Android.mk b/rsov/Android.mk
index 2991304..4afbb7c 100644
--- a/rsov/Android.mk
+++ b/rsov/Android.mk
@@ -2,7 +2,11 @@
 # Include Subdirectories
 #=====================================================================
 
+# Not building RSoV modules in PDK builds, as libSPIRV is not available in PDK.
+ifneq ($(TARGET_BUILD_PDK), true)
+
 LOCAL_PATH:=$(call my-dir)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
 
+endif # TARGET_BUILD_PDK
diff --git a/rsov/compiler/Android.mk b/rsov/compiler/Android.mk
index d78adfe..64fb073 100644
--- a/rsov/compiler/Android.mk
+++ b/rsov/compiler/Android.mk
@@ -25,6 +25,7 @@
 RS2SPIRV_SOURCES := \
   rs2spirv.cpp \
   Builtin.cpp \
+  Context.cpp \
   GlobalAllocPass.cpp \
   GlobalAllocSPIRITPass.cpp \
   GlobalMergePass.cpp \
@@ -51,6 +52,7 @@
 
 LOCAL_SRC_FILES := \
   Builtin.cpp \
+  Context.cpp \
   GlobalAllocSPIRITPass.cpp \
   RSAllocationUtils.cpp \
   Wrapper.cpp \
diff --git a/rsov/compiler/Context.cpp b/rsov/compiler/Context.cpp
new file mode 100644
index 0000000..79c13b2
--- /dev/null
+++ b/rsov/compiler/Context.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Context.h"
+
+#include "llvm/ADT/Triple.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace rs2spirv {
+
+Context &Context::getInstance() {
+  static Context c;
+  return c;
+}
+
+Context::Context() : mInitialized(false) {}
+
+bool Context::Initialize(std::unique_ptr<bcinfo::MetadataExtractor> ME) {
+  if (mInitialized) {
+    return true;
+  }
+
+  mMetadata = std::move(ME);
+
+  if (!mMetadata->extract()) {
+    llvm::errs() << "cannot extract metadata\n";
+    return false;
+  }
+
+  const char **varNames = mMetadata->getExportVarNameList();
+  size_t varCount = mMetadata->getExportVarCount();
+  mExportVarIndices.resize(varCount);
+
+  // Builds the lookup table from a variable name to its slot number
+  for (size_t slot = 0; slot < varCount; slot++) {
+    std::string varName(varNames[slot]);
+    mVarNameToSlot.insert(std::make_pair(varName, (uint32_t)slot));
+  }
+
+  const size_t kernelCount = mMetadata->getExportForEachSignatureCount();
+  const char **kernelNames = mMetadata->getExportForEachNameList();
+  for (size_t slot = 0; slot < kernelCount; slot++) {
+    mForEachNameToSlot.insert(std::make_pair(kernelNames[slot], slot));
+  }
+
+  mInitialized = true;
+
+  return true;
+}
+
+} // namespace rs2spirv
diff --git a/rsov/compiler/Context.h b/rsov/compiler/Context.h
new file mode 100644
index 0000000..cc86acc
--- /dev/null
+++ b/rsov/compiler/Context.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RS2SPIRV_CONTEXT_H
+#define RS2SPIRV_CONTEXT_H
+
+#include "RSAllocationUtils.h"
+#include "bcinfo/MetadataExtractor.h"
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <limits>
+#include <stdint.h>
+#include <vector>
+
+// Declare a friend relationship in a class with a test. Used rather that
+// FRIEND_TEST to avoid globally importing gtest/gtest.h into the main
+// RSoV header files.
+#ifdef __HOST__
+#define RSOV_FRIEND_TEST(test_set_name, individual_test)                       \
+  friend class test_set_name##_##individual_test##_Test
+#else
+#define RSOV_FRIEND_TEST(test_set_name, individual_test)
+#endif // __HOST__
+
+namespace bcinfo {
+class MetadataExtractor;
+}
+
+namespace llvm {
+class Module;
+}
+
+namespace rs2spirv {
+
+// A singleton that keeps state during the compilation from RS LLVM bitcode to
+// SPIR-V, which provides quick lookup of metadata and shares information
+// between the passes.
+class Context {
+  RSOV_FRIEND_TEST(ContextTest, testInitialize);
+
+public:
+  static Context &getInstance();
+
+  Context();
+
+  // Initialize the internal data struture such as the slot number lookup table,
+  // etc.
+  bool Initialize(std::unique_ptr<bcinfo::MetadataExtractor> ME);
+
+  // Returns the total number of exported variables
+  uint32_t getNumExportVar() const { return mExportVarIndices.size(); }
+
+  // Adds the mapping from the slot number of an exported variable to the index
+  // of its field in the global buffer
+  void addExportVarIndex(uint32_t slot, uint32_t index) {
+    mExportVarIndices[slot] = index;
+  }
+
+  // Adds the mapping from the name of an exported variable to the index of its
+  // field in the global buffer
+  void addExportVarIndex(const char *varName, uint32_t index) {
+    const uint32_t slot = getSlotForExportVar(varName);
+    if (slot == std::numeric_limits<uint32_t>::max()) {
+      assert(0 && "Invalid name for an exported variable");
+      return;
+    }
+    addExportVarIndex(slot, index);
+  }
+
+  // Given the slot number of an exported variable, returns the index of its
+  // field in the global buffer
+  uint32_t getExportVarIndex(uint32_t slot) const {
+    return mExportVarIndices[slot];
+  }
+
+  // Returns the total number of foreach kernels
+  uint32_t getNumForEachKernel() const { return mForEachNameToSlot.size(); }
+
+  // Checks if a name refers to a foreach kernel function
+  bool isForEachKernel(llvm::StringRef name) const {
+    return mForEachNameToSlot.count(name) != 0;
+  }
+
+  const bcinfo::MetadataExtractor &getMetadata() const { return *mMetadata; }
+
+  llvm::SmallVectorImpl<RSAllocationInfo> &getGlobalAllocs() {
+    return mGlobalAllocs;
+  }
+
+private:
+  uint32_t getSlotForExportVar(const char *varName) {
+    const llvm::StringRef strVarName(varName);
+    auto it = mVarNameToSlot.find(strVarName);
+    if (it == mVarNameToSlot.end()) {
+      return std::numeric_limits<uint32_t>::max();
+    }
+    return it->second;
+  }
+
+  bool mInitialized;
+  // RenderScript metadata embedded in the input LLVM Moduel
+  std::unique_ptr<bcinfo::MetadataExtractor> mMetadata;
+  // A map from exported variable names to their slot numbers
+  llvm::StringMap<uint32_t> mVarNameToSlot;
+  // A map from exported foreach kernel names to their slot numbers
+  llvm::StringMap<uint32_t> mForEachNameToSlot;
+  // These are the indices for each exported variable in the global buffer
+  std::vector<uint32_t> mExportVarIndices;
+  // For Global Allocations; carries global variable -> metadata offset
+  // mapping from an LLVM pass to a SPIRIT pass
+  llvm::SmallVector<RSAllocationInfo, 8> mGlobalAllocs;
+};
+
+} // namespace rs2spirv
+
+#endif // RS2SPIRV_CONTEXT_H
diff --git a/rsov/compiler/GlobalAllocPass.cpp b/rsov/compiler/GlobalAllocPass.cpp
index ef0b020..92c441a 100644
--- a/rsov/compiler/GlobalAllocPass.cpp
+++ b/rsov/compiler/GlobalAllocPass.cpp
@@ -16,13 +16,14 @@
 
 #include "GlobalAllocPass.h"
 
+#include "Context.h"
+#include "RSAllocationUtils.h"
+
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/Debug.h"
 
-#include "RSAllocationUtils.h"
-
 #define DEBUG_TYPE "rs2spirv-global-alloc"
 
 using namespace llvm;
@@ -60,7 +61,9 @@
 class GlobalAllocPass : public ModulePass {
 public:
   static char ID;
-  GlobalAllocPass() : ModulePass(ID) {}
+  GlobalAllocPass()
+      : ModulePass(ID), Allocs(Context::getInstance().getGlobalAllocs()) {}
+
   const char *getPassName() const override { return "GlobalAllocPass"; }
 
   bool runOnModule(Module &M) override {
@@ -72,7 +75,6 @@
     if (!CollectRes)
       return false; // Module not modified.
 
-    SmallVector<RSAllocationInfo, 8> Allocs;
     SmallVector<RSAllocationCallInfo, 8> Calls;
     getRSAllocationInfo(M, Allocs);
     getRSAllocAccesses(Allocs, Calls);
@@ -87,6 +89,9 @@
     DEBUG(dbgs() << "RS2SPIRVGlobalAllocPass end\n");
     return true;
   }
+
+private:
+  SmallVectorImpl<RSAllocationInfo> &Allocs;
 };
 
 // A simple pass to remove all global allocations forcibly
diff --git a/rsov/compiler/GlobalAllocSPIRITPass.cpp b/rsov/compiler/GlobalAllocSPIRITPass.cpp
index 61a7512..452f516 100644
--- a/rsov/compiler/GlobalAllocSPIRITPass.cpp
+++ b/rsov/compiler/GlobalAllocSPIRITPass.cpp
@@ -16,9 +16,12 @@
 
 #include "GlobalAllocSPIRITPass.h"
 
+#include "Context.h"
 #include "spirit.h"
 #include "transformer.h"
 
+#include <sstream>
+
 namespace android {
 namespace spirit {
 
@@ -29,21 +32,17 @@
 //  uint32_t element_size;
 //  uint32_t x_size;
 //  uint32_t y_size;
-//  uint32_t ??
+//  uint32_t unused
 // };
+
 VariableInst *AddGAMetadata(Builder &b, Module *m) {
   TypeIntInst *UInt32Ty = m->getUnsignedIntType(32);
-  std::vector<Instruction *> metadata{
-    UInt32Ty,
-    UInt32Ty,
-    UInt32Ty,
-    UInt32Ty
-  };
+  std::vector<Instruction *> metadata{UInt32Ty, UInt32Ty, UInt32Ty, UInt32Ty};
   auto MetadataStructTy = m->getStructType(metadata.data(), metadata.size());
   // FIXME: workaround on a weird OpAccessChain member offset problem. Somehow
-  // when given constant indices, OpAccessChain returns pointers that are 4 bytes
-  // less than what are supposed to be (at runtime).
-  // For now workaround this with +4 the member offsets.
+  // when given constant indices, OpAccessChain returns pointers that are 4
+  // bytes less than what are supposed to be (at runtime). For now workaround
+  // this with +4 the member offsets.
   MetadataStructTy->memberDecorate(0, Decoration::Offset)->addExtraOperand(4);
   MetadataStructTy->memberDecorate(1, Decoration::Offset)->addExtraOperand(8);
   MetadataStructTy->memberDecorate(2, Decoration::Offset)->addExtraOperand(12);
@@ -52,14 +51,14 @@
   // cannot use PushConstant underneath
   auto MetadataBufSTy = m->getRuntimeArrayType(MetadataStructTy);
   // Stride of metadata.
-  MetadataBufSTy->decorate(Decoration::ArrayStride)->addExtraOperand(
-      metadata.size()*sizeof(uint32_t));
+  MetadataBufSTy->decorate(Decoration::ArrayStride)
+      ->addExtraOperand(metadata.size() * sizeof(uint32_t));
   auto MetadataSSBO = m->getStructType(MetadataBufSTy);
   MetadataSSBO->decorate(Decoration::BufferBlock);
   auto MetadataPtrTy = m->getPointerType(StorageClass::Uniform, MetadataSSBO);
 
-
-  VariableInst *MetadataVar = b.MakeVariable(MetadataPtrTy, StorageClass::Uniform);
+  VariableInst *MetadataVar =
+      b.MakeVariable(MetadataPtrTy, StorageClass::Uniform);
   MetadataVar->decorate(Decoration::DescriptorSet)->addExtraOperand(0);
   MetadataVar->decorate(Decoration::Binding)->addExtraOperand(0);
   m->addVariable(MetadataVar);
@@ -67,6 +66,31 @@
   return MetadataVar;
 }
 
+std::string CreateGAIDMetadata(
+    const llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> &Allocs) {
+
+  std::stringstream mapping;
+  bool printed = false;
+
+  mapping << "{\"__RSoV_GA\": {";
+  for (auto &A : Allocs) {
+    // Skip unused GAs
+    if (!A.hasID()) {
+      continue;
+    }
+    if (printed)
+      mapping << ", ";
+    // "GA name" to the ID of the GA
+    mapping << "\"" << A.VarName.substr(1) << "\":" << A.ID;
+    printed = true;
+  }
+  mapping << "}}";
+
+  if (printed)
+    return mapping.str().c_str();
+  else
+    return "";
+}
 } // anonymous namespace
 
 // Replacing calls to lowered accessors, e.g., __rsov_rsAllocationGetDimX
@@ -101,7 +125,15 @@
 
 class GAAccessorTransformer : public Transformer {
 public:
+  GAAccessorTransformer()
+      : mBuilder(), mMetadata(nullptr),
+        mAllocs(rs2spirv::Context::getInstance().getGlobalAllocs()) {}
+
   std::vector<uint32_t> runAndSerialize(Module *module, int *error) override {
+    std::string GAMD = CreateGAIDMetadata(mAllocs);
+    if (GAMD.size() > 0) {
+      module->addString(GAMD.c_str());
+    }
     mMetadata = AddGAMetadata(mBuilder, module);
     return Transformer::runAndSerialize(module, error);
   }
@@ -141,6 +173,7 @@
 private:
   Builder mBuilder;
   VariableInst *mMetadata;
+  llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> &mAllocs;
 };
 
 } // namespace spirit
@@ -148,7 +181,7 @@
 
 namespace rs2spirv {
 
-android::spirit::Pass *CreateGAPass() {
+android::spirit::Pass *CreateGAPass(void) {
   return new android::spirit::GAAccessorTransformer();
 }
 
diff --git a/rsov/compiler/InlinePreparationPass.cpp b/rsov/compiler/InlinePreparationPass.cpp
index 5b0fa6d..fc1a50d 100644
--- a/rsov/compiler/InlinePreparationPass.cpp
+++ b/rsov/compiler/InlinePreparationPass.cpp
@@ -16,13 +16,12 @@
 
 #include "InlinePreparationPass.h"
 
-#include "llvm/ADT/StringSet.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
 
-#include "bcinfo/MetadataExtractor.h"
+#include "Context.h"
 
 #define DEBUG_TYPE "rs2spirv-inline"
 
@@ -33,53 +32,43 @@
 namespace {
 
 class InlinePreparationPass : public ModulePass {
-  bcinfo::MetadataExtractor &ME;
-
 public:
   static char ID;
-  explicit InlinePreparationPass(bcinfo::MetadataExtractor &Extractor)
-      : ModulePass(ID), ME(Extractor) {}
+  explicit InlinePreparationPass() : ModulePass(ID) {}
 
   const char *getPassName() const override { return "InlinePreparationPass"; }
 
   bool runOnModule(Module &M) override {
     DEBUG(dbgs() << "InlinePreparationPass\n");
 
-    const size_t RSKernelNum = ME.getExportForEachSignatureCount();
-    const char **RSKernelNames = ME.getExportForEachNameList();
-    if (RSKernelNum == 0)
-      DEBUG(dbgs() << "InlinePreparationPass detected no kernel\n");
-
-    StringSet<> KNames;
-    for (size_t i = 0; i < RSKernelNum; ++i)
-      KNames.insert(RSKernelNames[i]);
+    rs2spirv::Context &Ctxt = rs2spirv::Context::getInstance();
 
     for (auto &F : M.functions()) {
-      if (F.isDeclaration())
+      if (F.isDeclaration()) {
         continue;
+      }
 
-      const auto FName = F.getName();
-
-      // TODO: Consider inlining kernels (i.e. kernels calling other kernels)
-      // when multi-kernel module support is ready.
-      if (KNames.count(FName) != 0)
+      if (Ctxt.isForEachKernel(F.getName())) {
         continue; // Skip kernels.
+      }
 
       F.addFnAttr(Attribute::AlwaysInline);
       F.setLinkage(GlobalValue::InternalLinkage);
-      DEBUG(dbgs() << "Marked as alwaysinline:\t" << FName << '\n');
+
+      DEBUG(dbgs() << "Marked as alwaysinline:\t" << F.getName() << '\n');
     }
 
-    // Return true, as the pass modifies module.
+    // Returns true, because this pass modifies the Module.
     return true;
   }
 };
+
 } // namespace
 
 char InlinePreparationPass::ID = 0;
 
-ModulePass *createInlinePreparationPass(bcinfo::MetadataExtractor &ME) {
-  return new InlinePreparationPass(ME);
+ModulePass *createInlinePreparationPass() {
+  return new InlinePreparationPass();
 }
 
 } // namespace rs2spirv
diff --git a/rsov/compiler/InlinePreparationPass.h b/rsov/compiler/InlinePreparationPass.h
index aa85c43..12bbfa3 100644
--- a/rsov/compiler/InlinePreparationPass.h
+++ b/rsov/compiler/InlinePreparationPass.h
@@ -21,14 +21,9 @@
 class ModulePass;
 } // namespace llvm
 
-namespace bcinfo {
-class MetadataExtractor;
-} // namespace bcinfo
-
 namespace rs2spirv {
 
-llvm::ModulePass *
-createInlinePreparationPass(bcinfo::MetadataExtractor &Extractor);
+llvm::ModulePass *createInlinePreparationPass();
 
 } // namespace rs2spirv
 
diff --git a/rsov/compiler/RSAllocationUtils.cpp b/rsov/compiler/RSAllocationUtils.cpp
index b9d016b..8b66a6c 100644
--- a/rsov/compiler/RSAllocationUtils.cpp
+++ b/rsov/compiler/RSAllocationUtils.cpp
@@ -52,23 +52,27 @@
 
 bool getRSAllocationInfo(Module &M, SmallVectorImpl<RSAllocationInfo> &Allocs) {
   DEBUG(dbgs() << "getRSAllocationInfo\n");
-
   for (auto &GV : M.globals()) {
     if (GV.isDeclaration() || !isRSAllocation(GV))
       continue;
 
-    Allocs.push_back({'%' + GV.getName().str(), None, &GV});
+    Allocs.push_back({'%' + GV.getName().str(), None, &GV, -1});
   }
 
   return true;
 }
 
+// Collect Allocation access calls into the Calls
+// Also update Allocs with assigned ID.
+// After calling this function, Allocs would contain the mapping from
+// GV name to the corresponding ID.
 bool getRSAllocAccesses(SmallVectorImpl<RSAllocationInfo> &Allocs,
                         SmallVectorImpl<RSAllocationCallInfo> &Calls) {
   DEBUG(dbgs() << "getRSGEATCalls\n");
   DEBUG(dbgs() << "\n\n~~~~~~~~~~~~~~~~~~~~~\n\n");
 
   std::unordered_map<const Value *, const GlobalVariable *> Mapping;
+  int id_assigned = 0;
 
   for (auto &A : Allocs) {
     auto *GV = A.GlobalVar;
@@ -137,6 +141,9 @@
           const bool IsDIMX = DemangledNameRef.startswith(DIMXPrefix);
 
           assert(IsGEA || IsSEA || IsDIMX);
+          if (!A.hasID()) {
+            A.assignID(id_assigned++);
+          }
 
           if (IsGEA || IsSEA) {
             DEBUG(dbgs() << "Found rsAlloc function!\n");
diff --git a/rsov/compiler/RSAllocationUtils.h b/rsov/compiler/RSAllocationUtils.h
index a3c22d1..b25cfde 100644
--- a/rsov/compiler/RSAllocationUtils.h
+++ b/rsov/compiler/RSAllocationUtils.h
@@ -35,6 +35,13 @@
   std::string VarName;
   llvm::Optional<std::string> RSElementType;
   llvm::GlobalVariable *GlobalVar;
+  // Assigned unique identifier for this allocation;
+  // not the slot #.
+  // Represents the index of this allocation's metadata
+  // in the global allocation metadata Vulkan buffer
+  int ID;
+  bool hasID(void) const { return ID >= 0; }
+  void assignID(int no) { ID = no; }
 };
 
 enum class RSAllocAccessKind { GEA, SEA, DIMX };
diff --git a/rsov/compiler/RSSPIRVWriter.cpp b/rsov/compiler/RSSPIRVWriter.cpp
index 200c388..22e739d 100644
--- a/rsov/compiler/RSSPIRVWriter.cpp
+++ b/rsov/compiler/RSSPIRVWriter.cpp
@@ -16,17 +16,8 @@
 
 #include "RSSPIRVWriter.h"
 
-#include "llvm/ADT/Triple.h"
-#include "llvm/IR/LegacyPassManager.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/SPIRV.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO.h"
-#include "llvm/Transforms/Scalar.h"
-
 #include "Builtin.h"
+#include "Context.h"
 #include "GlobalAllocPass.h"
 #include "GlobalAllocSPIRITPass.h"
 #include "GlobalMergePass.h"
@@ -37,6 +28,16 @@
 #include "bcinfo/MetadataExtractor.h"
 #include "pass_queue.h"
 
+#include "llvm/ADT/Triple.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/SPIRV.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/Scalar.h"
+
 #define DEBUG_TYPE "rs2spirv-writer"
 
 using namespace llvm;
@@ -44,7 +45,9 @@
 
 namespace rs2spirv {
 
-static void HandleTargetTriple(llvm::Module &M) {
+namespace {
+
+void HandleTargetTriple(llvm::Module &M) {
   Triple TT(M.getTargetTriple());
   auto Arch = TT.getArch();
 
@@ -68,11 +71,12 @@
   M.setTargetTriple(NewTriple);
 }
 
-void addPassesForRS2SPIRV(llvm::legacy::PassManager &PassMgr,
-                          bcinfo::MetadataExtractor &Extractor) {
-  PassMgr.add(createInlinePreparationPass(Extractor));
+} // namespace
+
+void addPassesForRS2SPIRV(llvm::legacy::PassManager &PassMgr) {
+  PassMgr.add(createInlinePreparationPass());
   PassMgr.add(createAlwaysInlinerPass());
-  PassMgr.add(createRemoveNonkernelsPass(Extractor));
+  PassMgr.add(createRemoveNonkernelsPass());
   // Delete unreachable globals.
   PassMgr.add(createGlobalDCEPass());
   // Remove dead debug info.
@@ -96,27 +100,29 @@
   PassMgr.add(createSPIRVLowerBool());
 }
 
-bool WriteSPIRV(llvm::Module *M, llvm::raw_ostream &OS, std::string &ErrMsg) {
-  std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule());
-
+bool WriteSPIRV(llvm::Module *M, std::unique_ptr<bcinfo::MetadataExtractor> ME,
+                llvm::raw_ostream &OS, std::string &ErrMsg) {
   HandleTargetTriple(*M);
 
-  bcinfo::MetadataExtractor ME(M);
-  if (!ME.extract()) {
-    errs() << "Could not extract metadata\n";
+  Context &Ctxt = Context::getInstance();
+
+  if (!Ctxt.Initialize(std::move(ME))) {
+    ErrMsg = "Failed to intialize rs2spirv";
     return false;
   }
-  DEBUG(dbgs() << "Metadata extracted\n");
 
   llvm::legacy::PassManager PassMgr;
-  addPassesForRS2SPIRV(PassMgr, ME);
+  addPassesForRS2SPIRV(PassMgr);
+
+  std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule());
 
   PassMgr.add(createLLVMToSPIRV(BM.get()));
   PassMgr.run(*M);
   DEBUG(M->dump());
 
-  if (BM->getError(ErrMsg) != SPIRVEC_Success)
+  if (BM->getError(ErrMsg) != SPIRVEC_Success) {
     return false;
+  }
 
   llvm::SmallString<4096> O;
   llvm::raw_svector_ostream SVOS(O);
@@ -129,7 +135,7 @@
   memcpy(words.data(), str.data(), str.size());
 
   android::spirit::PassQueue spiritPasses;
-  spiritPasses.append(CreateWrapperPass(ME, *M));
+  spiritPasses.append(CreateWrapperPass(*M));
   spiritPasses.append(CreateBuiltinPass());
   spiritPasses.append(CreateGAPass());
 
@@ -138,10 +144,12 @@
 
   if (error != 0) {
     OS << *BM;
+    ErrMsg = "Failed to generate wrappers for kernels";
     return false;
   }
 
-  OS.write(reinterpret_cast<const char*>(wordsOut.data()), wordsOut.size() * 4);
+  OS.write(reinterpret_cast<const char *>(wordsOut.data()),
+           wordsOut.size() * 4);
 
   return true;
 }
diff --git a/rsov/compiler/RSSPIRVWriter.h b/rsov/compiler/RSSPIRVWriter.h
index 91fe4d0..4b0d80c 100644
--- a/rsov/compiler/RSSPIRVWriter.h
+++ b/rsov/compiler/RSSPIRVWriter.h
@@ -24,9 +24,14 @@
 class raw_ostream;
 } // namespace llvm
 
+namespace bcinfo {
+class MetadataExtractor;
+} // namespace bcinfo
+
 namespace rs2spirv {
 
-bool WriteSPIRV(llvm::Module *M, llvm::raw_ostream &OS, std::string &ErrMsg);
+bool WriteSPIRV(llvm::Module *M, std::unique_ptr<bcinfo::MetadataExtractor> ME,
+                llvm::raw_ostream &OS, std::string &ErrMsg);
 
 } // namespace rs2spirv
 
diff --git a/rsov/compiler/RemoveNonkernelsPass.cpp b/rsov/compiler/RemoveNonkernelsPass.cpp
index 09cf1ae..fa1e8e3 100644
--- a/rsov/compiler/RemoveNonkernelsPass.cpp
+++ b/rsov/compiler/RemoveNonkernelsPass.cpp
@@ -16,14 +16,13 @@
 
 #include "RemoveNonkernelsPass.h"
 
-#include "llvm/ADT/StringSet.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
 
-#include "bcinfo/MetadataExtractor.h"
+#include "Context.h"
 
 #define DEBUG_TYPE "rs2spirv-remove"
 
@@ -34,12 +33,9 @@
 namespace {
 
 class RemoveNonkernelsPass : public ModulePass {
-  bcinfo::MetadataExtractor &ME;
-
 public:
   static char ID;
-  explicit RemoveNonkernelsPass(bcinfo::MetadataExtractor &Extractor)
-      : ModulePass(ID), ME(Extractor) {}
+  explicit RemoveNonkernelsPass() : ModulePass(ID) {}
 
   const char *getPassName() const override { return "RemoveNonkernelsPass"; }
 
@@ -47,14 +43,13 @@
     DEBUG(dbgs() << "RemoveNonkernelsPass\n");
     DEBUG(M.dump());
 
-    const size_t RSKernelNum = ME.getExportForEachSignatureCount();
-    const char **RSKernelNames = ME.getExportForEachNameList();
-    if (RSKernelNum == 0)
-      DEBUG(dbgs() << "RemoveNonkernelsPass detected no kernel\n");
+    rs2spirv::Context &Ctxt = rs2spirv::Context::getInstance();
 
-    StringSet<> KNames;
-    for (size_t i = 0; i < RSKernelNum; ++i)
-      KNames.insert(RSKernelNames[i]);
+    if (Ctxt.getNumForEachKernel() == 0) {
+      DEBUG(dbgs() << "RemoveNonkernelsPass detected no kernel\n");
+      // Returns false, since no modification is made to the Module.
+      return false;
+    }
 
     std::vector<Function *> Functions;
     for (auto &F : M.functions()) {
@@ -65,30 +60,30 @@
       if (F->isDeclaration())
         continue;
 
-      const StringRef FName = F->getName();
-
-      if (KNames.count(FName) != 0)
+      if (Ctxt.isForEachKernel(F->getName())) {
         continue; // Skip kernels.
+      }
 
       F->replaceAllUsesWith(UndefValue::get((Type *)F->getType()));
       F->eraseFromParent();
 
-      DEBUG(dbgs() << "Removed:\t" << FName << '\n');
+      DEBUG(dbgs() << "Removed:\t" << F->getName() << '\n');
     }
 
-    // Return true, as the pass modifies module.
     DEBUG(M.dump());
     DEBUG(dbgs() << "Done removal\n");
 
+    // Returns true, because the pass modifies the Module.
     return true;
   }
 };
+
 } // namespace
 
 char RemoveNonkernelsPass::ID = 0;
 
-ModulePass *createRemoveNonkernelsPass(bcinfo::MetadataExtractor &ME) {
-  return new RemoveNonkernelsPass(ME);
+ModulePass *createRemoveNonkernelsPass() {
+  return new RemoveNonkernelsPass();
 }
 
 } // namespace rs2spirv
diff --git a/rsov/compiler/RemoveNonkernelsPass.h b/rsov/compiler/RemoveNonkernelsPass.h
index aa91c91..eed2dc8 100644
--- a/rsov/compiler/RemoveNonkernelsPass.h
+++ b/rsov/compiler/RemoveNonkernelsPass.h
@@ -21,14 +21,9 @@
 class ModulePass;
 } // namespace llvm
 
-namespace bcinfo {
-class MetadataExtractor;
-} // namespace bcinfo
-
 namespace rs2spirv {
 
-llvm::ModulePass *
-createRemoveNonkernelsPass(bcinfo::MetadataExtractor &Extractor);
+llvm::ModulePass *createRemoveNonkernelsPass();
 
 } // namespace rs2spirv
 
diff --git a/rsov/compiler/Wrapper.cpp b/rsov/compiler/Wrapper.cpp
index 77a7806..1bcb47e 100644
--- a/rsov/compiler/Wrapper.cpp
+++ b/rsov/compiler/Wrapper.cpp
@@ -19,6 +19,7 @@
 #include "llvm/IR/Module.h"
 
 #include "Builtin.h"
+#include "Context.h"
 #include "GlobalAllocSPIRITPass.h"
 #include "RSAllocationUtils.h"
 #include "bcinfo/MetadataExtractor.h"
@@ -27,6 +28,7 @@
 #include "module.h"
 #include "pass.h"
 
+#include <sstream>
 #include <vector>
 
 using bcinfo::MetadataExtractor;
@@ -272,7 +274,6 @@
         std::cerr << "struct layout is null" << std::endl;
         return false;
       }
-
       for (uint32_t i = 0, e = LStructTy->getNumElements(); i != e; ++i) {
         auto decor = StructTy->memberDecorate(i, Decoration::Offset);
         if (!decor) {
@@ -280,7 +281,8 @@
                     << std::endl;
           return false;
         }
-        decor->addExtraOperand((uint32_t)SLayout->getElementOffset(i));
+        const uint32_t offset = (uint32_t)SLayout->getElementOffset(i);
+        decor->addExtraOperand(offset);
       }
     }
   }
@@ -352,9 +354,10 @@
 
 } // anonymous namespace
 
-bool AddWrappers(const bcinfo::MetadataExtractor &metadata,
-                 llvm::Module &LM,
+bool AddWrappers(llvm::Module &LM,
                  android::spirit::Module *m) {
+  rs2spirv::Context &Ctxt = rs2spirv::Context::getInstance();
+  const bcinfo::MetadataExtractor &metadata = Ctxt.getMetadata();
   android::spirit::Builder b;
 
   m->setBuilder(&b);
@@ -384,12 +387,10 @@
 
 class WrapperPass : public Pass {
 public:
-  WrapperPass(const bcinfo::MetadataExtractor &Metadata,
-              const llvm::Module &LM) : mLLVMMetadata(Metadata),
-                                        mLLVMModule(const_cast<llvm::Module&>(LM)) {}
+  WrapperPass(const llvm::Module &LM) : mLLVMModule(const_cast<llvm::Module&>(LM)) {}
 
   Module *run(Module *m, int *error) override {
-    bool success = AddWrappers(mLLVMMetadata, mLLVMModule, m);
+    bool success = AddWrappers(mLLVMModule, m);
     if (error) {
       *error = success ? 0 : -1;
     }
@@ -397,7 +398,6 @@
   }
 
 private:
-  const bcinfo::MetadataExtractor &mLLVMMetadata;
   llvm::Module &mLLVMModule;
 };
 
@@ -406,9 +406,8 @@
 
 namespace rs2spirv {
 
-android::spirit::Pass* CreateWrapperPass(const bcinfo::MetadataExtractor &metadata,
-                                         const llvm::Module &LLVMModule) {
-  return new android::spirit::WrapperPass(metadata, LLVMModule);
+android::spirit::Pass* CreateWrapperPass(const llvm::Module &LLVMModule) {
+  return new android::spirit::WrapperPass(LLVMModule);
 }
 
 } // namespace rs2spirv
diff --git a/rsov/compiler/Wrapper.h b/rsov/compiler/Wrapper.h
index ca2a116..27aaee8 100644
--- a/rsov/compiler/Wrapper.h
+++ b/rsov/compiler/Wrapper.h
@@ -19,10 +19,6 @@
 
 #include <stdint.h>
 
-namespace bcinfo {
-class MetadataExtractor;
-}
-
 namespace llvm {
 class Module;
 }
@@ -53,8 +49,7 @@
 
 namespace rs2spirv {
 
-android::spirit::Pass* CreateWrapperPass(const bcinfo::MetadataExtractor &metadata,
-                                         const llvm::Module &LLVMModule);
+android::spirit::Pass* CreateWrapperPass(const llvm::Module &LLVMModule);
 
 } // namespace rs2spirv
 
diff --git a/rsov/compiler/rs2spirv.cpp b/rsov/compiler/rs2spirv.cpp
index 69d75a5..6701d27 100644
--- a/rsov/compiler/rs2spirv.cpp
+++ b/rsov/compiler/rs2spirv.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include "RSSPIRVWriter.h"
+#include "bcinfo/MetadataExtractor.h"
+#include "spirit/file_utils.h"
+
 #include "llvm/Bitcode/ReaderWriter.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
@@ -26,8 +30,6 @@
 #include "llvm/Support/ToolOutputFile.h"
 #include "llvm/Support/raw_ostream.h"
 
-#include "RSSPIRVWriter.h"
-
 #define DEBUG_TYPE "rs2spirv"
 
 namespace kExt {
@@ -59,7 +61,6 @@
     errs() << "Fails to open input file: " << Err;
     return -1;
   }
-
   ErrorOr<std::unique_ptr<Module>> MOrErr =
       getStreamedBitcodeModule(InputFile, std::move(DS), Context);
 
@@ -85,8 +86,13 @@
   llvm::StringRef outFile(OutputFile);
   std::error_code EC;
   llvm::raw_fd_ostream OFS(outFile, EC, llvm::sys::fs::F_None);
-  if (!rs2spirv::WriteSPIRV(M.get(), OFS, Err)) {
-    errs() << "Fails to save LLVM as SPIRV: " << Err << '\n';
+
+  std::vector<char> bitcode = android::spirit::readFile<char>(InputFile);
+  std::unique_ptr<bcinfo::MetadataExtractor> ME(
+      new bcinfo::MetadataExtractor(bitcode.data(), bitcode.size()));
+
+  if (!rs2spirv::WriteSPIRV(M.get(), std::move(ME), OFS, Err)) {
+    errs() << "compiler error: " << Err << '\n';
     return -1;
   }
 
diff --git a/rsov/compiler/tests/rs_allocation/getdimx_64.ll b/rsov/compiler/tests/rs_allocation/getdimx_64.ll
index f5976fb..6350b62 100644
--- a/rsov/compiler/tests/rs_allocation/getdimx_64.ll
+++ b/rsov/compiler/tests/rs_allocation/getdimx_64.ll
@@ -7,6 +7,7 @@
 target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
 target triple = "aarch64-none-linux-gnueabi"
 
+; CHECK: OpString "{\"__RSoV_GA\": {\"g\":0}}"
 ; CHECK: OpMemberDecorate [[MetadataS:%[a-zA-Z_0-9]*]] 0 Offset 4
 ; CHECK: OpMemberDecorate [[MetadataS]] 1 Offset 8
 ; CHECK: OpMemberDecorate [[MetadataS]] 2 Offset 12
diff --git a/rsov/driver/Android.mk b/rsov/driver/Android.mk
index 3183195..838a7e6 100644
--- a/rsov/driver/Android.mk
+++ b/rsov/driver/Android.mk
@@ -37,13 +37,15 @@
     libbcinfo \
     libc++ \
     liblog \
+    libspirit \
     libvulkan
 
 LOCAL_C_INCLUDES := \
     frameworks/compile/libbcc/include \
     frameworks/native/vulkan/include \
     frameworks/rs \
-    frameworks/rs/cpu_ref
+    frameworks/rs/cpu_ref \
+    frameworks/rs/rsov/compiler \
 
 LOCAL_C_INCLUDES += \
 
diff --git a/rsov/driver/rsovScript.cpp b/rsov/driver/rsovScript.cpp
index d67b38c..f896c34 100644
--- a/rsov/driver/rsovScript.cpp
+++ b/rsov/driver/rsovScript.cpp
@@ -18,19 +18,31 @@
 
 #include "bcinfo/MetadataExtractor.h"
 #include "rsContext.h"
+#include "rsDefines.h"
 #include "rsType.h"
 #include "rsUtils.h"
 #include "rsovAllocation.h"
 #include "rsovContext.h"
 #include "rsovCore.h"
+#include "spirit/instructions.h"
+#include "spirit/module.h"
 
 #include <fstream>
+#include <functional>
 
 namespace android {
 namespace renderscript {
 namespace rsov {
 
 namespace {
+// Layout of this struct has to be the same as the struct in generated SPIR-V
+// TODO: generate this file from some spec that is shared with the compiler
+struct rsovTypeInfo {
+  uint32_t element_size;  // TODO: not implemented
+  uint32_t x_size;
+  uint32_t y_size;
+  uint32_t z_size;
+};
 
 const char *COMPILER_EXE_PATH = "/system/bin/bcc_rsov";
 
@@ -120,11 +132,14 @@
 }
 
 RSoVScript::RSoVScript(RSoVContext *context, std::vector<uint32_t> &&spvWords,
-                       bcinfo::MetadataExtractor *ME)
+                       bcinfo::MetadataExtractor *ME,
+                       std::map<std::string, int> *GA2ID)
     : mRSoV(context),
       mDevice(context->getDevice()),
       mSPIRVWords(std::move(spvWords)),
-      mME(ME) {}
+      mME(ME),
+      mGlobalAllocationMetadata(nullptr),
+      mGAMapping(GA2ID) {}
 
 RSoVScript::~RSoVScript() {
   delete mCpuScript;
@@ -174,10 +189,12 @@
 void RSoVScript::setGlobalVar(uint32_t slot, const void *data,
                               size_t dataLength) {
   // TODO: implement this
+  ALOGV("%s missing.", __FUNCTION__);
 }
 
 void RSoVScript::getGlobalVar(uint32_t slot, void *data, size_t dataLength) {
   // TODO: implement this
+  ALOGV("%s missing.", __FUNCTION__);
 }
 
 void RSoVScript::setGlobalVarWithElemDims(uint32_t slot, const void *data,
@@ -188,11 +205,13 @@
 }
 
 void RSoVScript::setGlobalBind(uint32_t slot, Allocation *data) {
+  ALOGV("%s succeeded.", __FUNCTION__);
   // TODO: implement this
 }
 
 void RSoVScript::setGlobalObj(uint32_t slot, ObjectBase *obj) {
-  // TODO: implement this
+  mCpuScript->setGlobalObj(slot, obj);
+  ALOGV("%s succeeded.", __FUNCTION__);
 }
 
 Allocation *RSoVScript::getAllocationForPointer(const void *ptr) const {
@@ -230,6 +249,14 @@
   // TODO: kernels with zero output allocations
   std::vector<VkDescriptorSetLayoutBinding> layout_bindings{
       {
+          // for the global allocation metadata
+          .binding = 0,
+          .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+          .descriptorCount = 1,
+          .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+          .pImmutableSamplers = nullptr,
+      },
+      {
           // for the output allocation
           .binding = 1,
           .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
@@ -254,7 +281,7 @@
       .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
       .pNext = nullptr,
       .flags = 0,
-      .bindingCount = inLen + 1,
+      .bindingCount = inLen + 2,
       .pBindings = layout_bindings.data(),
   };
 
@@ -324,7 +351,8 @@
   VkResult res;
   VkDescriptorPoolSize type_count[] = {
       {
-          .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1 + inLen,
+          .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+          .descriptorCount = 2 + inLen,
       },
   };
 
@@ -342,6 +370,43 @@
   ALOGV("%s succeeded.", __FUNCTION__);
 }
 
+// Iterate through a list of global allocations that are used inside the module
+// and marshal their type information to a dedicated Vulkan Buffer
+void RSoVScript::MarshalTypeInfo(void) {
+  // Marshal global allocation metadata to the device
+  auto *cs = getCpuScript();
+  int nr_globals = mGAMapping->size();
+  if (mGlobalAllocationMetadata == nullptr) {
+    mGlobalAllocationMetadata.reset(
+        new RSoVBuffer(mRSoV, sizeof(struct rsovTypeInfo) * nr_globals));
+  }
+  struct rsovTypeInfo *mappedMetadata =
+      (struct rsovTypeInfo *)mGlobalAllocationMetadata->getHostPtr();
+  for (int i = 0; i < nr_globals; ++i) {
+    if (getGlobalRsType(cs->getGlobalProperties(i)) ==
+        RsDataType::RS_TYPE_ALLOCATION) {
+      ALOGV("global variable %d is an allocation!", i);
+      const void *host_buf;
+      cs->getGlobalVar(i, (void *)&host_buf, sizeof(host_buf));
+      if (!host_buf) continue;
+      const android::renderscript::Allocation *GA =
+          static_cast<const android::renderscript::Allocation *>(host_buf);
+      const android::renderscript::Type *T = GA->getType();
+      rsAssert(T);
+
+      auto global_it = mGAMapping->find(cs->getGlobalName(i));
+      rsAssert(global_it != (*mGAMapping).end());
+      int id = global_it->second;
+      ALOGV("global allocation %s is mapped to ID %d", cs->getGlobalName(i),
+            id);
+      // TODO: marshal other properties
+      mappedMetadata[id].x_size = T->getDimX();
+      mappedMetadata[id].y_size = T->getDimY();
+      mappedMetadata[id].z_size = T->getDimZ();
+    }
+  }
+}
+
 void RSoVScript::InitDescriptorSet(
     const std::vector<RSoVAllocation *> &inputAllocations,
     RSoVAllocation *outputAllocation) {
@@ -363,6 +428,17 @@
   // TODO: support for set up the binding(s) of global variables
   uint32_t nBindings = inputAllocations.size() + 1;  // input + output.
   std::vector<VkWriteDescriptorSet> writes{
+      // Metadata for global allocations
+      {
+          .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+          .dstSet = mDescSet[0],
+          .dstBinding = 0,
+          .dstArrayElement = 0,
+          .descriptorCount = 1,
+          .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+          .pBufferInfo = mGlobalAllocationMetadata->getBufferInfo(),
+      },
+
       {
           .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
           .dstSet = mDescSet[0],
@@ -420,6 +496,7 @@
   InitDescriptorAndPipelineLayouts(inLen);
   InitShader(slot);
   InitDescriptorPool(inLen);
+  MarshalTypeInfo();
   InitDescriptorSet(inputAllocations, outputAllocation);
   // InitPipelineCache();
   InitPipeline();
@@ -495,9 +572,10 @@
   for (int i = 0; i < NUM_DESCRIPTOR_SETS; i++)
     vkDestroyDescriptorSetLayout(mDevice, mDescLayout[i], nullptr);
   vkDestroyPipelineLayout(mDevice, mPipelineLayout, nullptr);
-  vkFreeDescriptorSets(mDevice, mDescPool, NUM_DESCRIPTOR_SETS, mDescSet.data());
+  vkFreeDescriptorSets(mDevice, mDescPool, NUM_DESCRIPTOR_SETS,
+                       mDescSet.data());
   vkDestroyDescriptorPool(mDevice, mDescPool, nullptr);
-  free((void*)mShaderStage.pName);
+  free((void *)mShaderStage.pName);
   vkDestroyShaderModule(mDevice, mShaderStage.module, nullptr);
 
   ALOGV("%s succeeded.", __FUNCTION__);
@@ -519,6 +597,91 @@
 using android::renderscript::rsov::RSoVScript;
 using android::renderscript::rsov::compileBitcode;
 
+namespace {
+// A class to parse global allocation metadata; essentially a subset of JSON
+// it would look like {"__RSoV_GA": {"g":42}}
+// The result is stored in a refence to a map<string, int>
+class ParseMD {
+ public:
+  ParseMD(std::string s, std::map<std::string, int> &map)
+      : mString(s), mMapping(map) {}
+
+  bool parse(void) {
+    // remove outermose two pairs of braces
+    mString = removeBraces(mString);
+    mString = removeBraces(mString);
+    // Now we are supposed to have a comma-separated list that looks like:
+    // "foo":42, "bar":56
+    split<','>(mString, [&](auto s) {
+      split<':'>(s, nullptr, [&](auto pair) {
+        rsAssert(pair.size() == 2);
+        std::string ga_name = removeQuotes(pair[0]);
+        int id = atoi(pair[1].c_str());
+        ALOGV("ParseMD: global allocation %s has ID %d", ga_name.c_str(), id);
+        mMapping[ga_name] = id;
+      });
+    });
+    return true;
+  }
+
+ private:
+  template <char L, char R>
+  static std::string removeMatching(const std::string &s) {
+    auto leftCBrace = s.find(L);
+    rsAssert(leftCBrace != std::string::npos);
+    leftCBrace++;
+    return s.substr(leftCBrace, s.rfind(R) - leftCBrace);
+  }
+
+  static std::string removeBraces(const std::string &s) {
+    return removeMatching<'{', '}'>(s);
+  }
+
+  static std::string removeQuotes(const std::string &s) {
+    return removeMatching<'"', '"'>(s);
+  }
+
+  // Splitting a string, and call "each" and/or "all" with individal elements
+  // and a vector of all tokenized elements
+  template <char D>
+  static void split(const std::string &s,
+                    std::function<void(const std::string &)> each,
+                    std::function<void(const std::vector<const std::string> &)>
+                        all = nullptr) {
+    std::vector<const std::string> result;
+    for (std::string::size_type pos = 0; pos < s.size(); pos++) {
+      std::string::size_type begin = pos;
+
+      while (s[pos] != D && pos <= s.size()) pos++;
+      std::string found = s.substr(begin, pos - begin);
+      if (each) each(found);
+      if (all) result.push_back(found);
+    }
+    if (all) all(result);
+  }
+
+  std::string mString;
+  std::map<std::string, int> &mMapping;
+};
+
+}  // namespace
+
+class ExtractRSoVMD : public android::spirit::DoNothingVisitor {
+ public:
+  ExtractRSoVMD() : mGAMapping(new std::map<std::string, int>) {}
+
+  void visit(android::spirit::StringInst *s) {
+    ALOGV("ExtractRSoVMD: string = %s", s->mOperand1.c_str());
+    ParseMD p(s->mOperand1, *mGAMapping);
+    p.parse();
+  }
+
+  std::map<std::string, int> *takeMapping(void) { return mGAMapping.release(); }
+
+ private:
+  std::unique_ptr<std::map<std::string, int> > mGAMapping;
+};
+
 bool rsovScriptInit(const Context *rsc, ScriptC *script, char const *resName,
                     char const *cacheDir, uint8_t const *bitcode,
                     size_t bitcodeSize, uint32_t flags) {
@@ -543,8 +706,15 @@
   auto spvWords =
       compileBitcode(resName, cacheDir, (const char *)bitcode, bitcodeSize);
   if (!spvWords.empty()) {
-    RSoVScript *rsovScript = new RSoVScript(hal->mRSoV, std::move(spvWords),
-                                            bitcodeMetadata.release());
+    // Extract compiler metadata on allocation->binding mapping
+    android::spirit::Module *module =
+        android::spirit::Deserialize<android::spirit::Module>(spvWords);
+    rsAssert(module);
+    ExtractRSoVMD ga_md;
+    module->accept(&ga_md);
+    RSoVScript *rsovScript =
+        new RSoVScript(hal->mRSoV, std::move(spvWords),
+                       bitcodeMetadata.release(), ga_md.takeMapping());
     if (rsovScript) {
       rsovScript->setCpuScript(cs.release());
       RSoVScript::initScriptOnRSoV(script, rsovScript);
diff --git a/rsov/driver/rsovScript.h b/rsov/driver/rsovScript.h
index 19bb84c..83205a3 100644
--- a/rsov/driver/rsovScript.h
+++ b/rsov/driver/rsovScript.h
@@ -19,6 +19,7 @@
 
 #include <vulkan/vulkan.h>
 
+#include <map>
 #include <vector>
 
 #include "bcinfo/MetadataExtractor.h"
@@ -38,15 +39,18 @@
 namespace rsov {
 
 class RSoVAllocation;
+class RSoVBuffer;
 class RSoVContext;
 
 // TODO: CpuScript is a bad name for the base class. Fix with a refactoring.
 class RSoVScript : RsdCpuReference::CpuScript {
  public:
   RSoVScript(RSoVContext *context, std::vector<uint32_t> &&spvWords,
-             bcinfo::MetadataExtractor *ME);
+             bcinfo::MetadataExtractor *ME,
+             std::map<std::string, int> *GAMapping);
   RSoVScript(RSoVContext *context, const std::vector<uint32_t> &spvWords,
-             bcinfo::MetadataExtractor *ME) = delete;
+             bcinfo::MetadataExtractor *ME,
+             std::map<std::string, int> *GAMapping) = delete;
 
   virtual ~RSoVScript();
 
@@ -106,6 +110,7 @@
                          RSoVAllocation *outputAllocation);
   void InitPipelineCache();
   void InitPipeline();
+  void MarshalTypeInfo();
   void runForEach(uint32_t slot, uint32_t inLen,
                   const std::vector<RSoVAllocation *> &input,
                   RSoVAllocation *output);
@@ -127,6 +132,10 @@
   std::vector<VkDescriptorSet> mDescSet;
   // For kernel names
   const bcinfo::MetadataExtractor *mME;
+  // Metadata of global allocations
+  std::unique_ptr<RSoVBuffer> mGlobalAllocationMetadata;
+  // Mapping of global allocation to rsov-assigned ID
+  std::unique_ptr<std::map<std::string, int> > mGAMapping;
 };
 
 }  // namespace rsov
diff --git a/rsov/tests/Android.mk b/rsov/tests/Android.mk
new file mode 100644
index 0000000..06657c2
--- /dev/null
+++ b/rsov/tests/Android.mk
@@ -0,0 +1,12 @@
+#=====================================================================
+# Include Subdirectories
+#=====================================================================
+
+# Not building RSoV tests in PDK builds.
+ifneq ($(TARGET_BUILD_PDK), true)
+
+LOCAL_PATH:=$(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
+endif # TARGET_BUILD_PDK
diff --git a/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/RSoVTestCore.java b/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/RSoVTestCore.java
index 40bd307..a25b7ad 100644
--- a/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/RSoVTestCore.java
+++ b/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/RSoVTestCore.java
@@ -58,6 +58,7 @@
         unitTests.add(new UT_modulo(this, mCtx));
         unitTests.add(new UT_multi_kernel(this, mCtx));
         unitTests.add(new UT_multi_input(this, mCtx));
+        unitTests.add(new UT_global_query(this, mCtx));
 
         UnitTest[] uta = new UnitTest[unitTests.size()];
         uta = unitTests.toArray(uta);
diff --git a/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/UT_global_query.java b/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/UT_global_query.java
new file mode 100644
index 0000000..821f9d2
--- /dev/null
+++ b/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/UT_global_query.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.rsov.test;
+
+import android.content.Context;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+public class UT_global_query extends UnitTest {
+    protected UT_global_query(RSoVTestCore rstc, Context ctx) {
+        super(rstc, "global_query", ctx);
+    }
+
+    private boolean Test(int width, int height, int depth) {
+        RenderScript pRS = RenderScript.create(mCtx);
+        ScriptC_global_query s = new ScriptC_global_query(pRS);
+
+        Type.Builder typeBuilder = new Type.Builder(pRS, Element.I32(pRS));
+        typeBuilder.setX(width);
+        if (height > 0) {
+            typeBuilder.setY(height);
+            if (depth > 0) {
+                typeBuilder.setZ(depth);
+            }
+        }
+
+        if (depth < 1) {
+            depth = 1;
+        }
+
+        if (height < 1) {
+            height = 1;
+        }
+
+        Allocation G = Allocation.createTyped(pRS, typeBuilder.create());
+        Allocation dummy = Allocation.createTyped(pRS, typeBuilder.create());
+        Allocation R = Allocation.createTyped(pRS, typeBuilder.create());
+
+        int g[] = new int[width * height * depth];
+        int d[] = new int[width * height * depth];
+        int r[] = new int[width * height * depth];
+
+        java.util.Random rand = new java.util.Random();
+
+        for (int i = 0; i < width * height * depth; i++) {
+            g[i] = rand.nextInt(123456);
+            d[i] = rand.nextInt(123456);
+        }
+
+        G.copyFrom(g);
+        dummy.copyFrom(d);
+
+        s.set_g(G);
+
+        s.forEach_getDim(dummy, R);
+
+        R.copyTo(r);
+
+        R.destroy();
+        dummy.destroy();
+        G.destroy();
+
+        pRS.finish();
+        pRS.destroy();
+
+        boolean failed = false;
+        for (int i = 0; i < width * height * depth; i++) {
+            if (r[i] != width) {
+                Log.e(name, "expects " + width + " for element " + i +
+                      ". got " + r[i]);
+                failed = true;
+                break;
+            }
+        }
+
+        return !failed;
+    }
+
+    public void run() {
+        final int X = 96;
+
+        if (Test(X, 0, 0)) {
+            passTest();
+            return;
+        }
+
+        failTest();
+    }
+}
diff --git a/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/global_query.rs b/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/global_query.rs
new file mode 100644
index 0000000..c15a0b9
--- /dev/null
+++ b/rsov/tests/RSoVTest/src/com/android/rs/rsov/test/global_query.rs
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.rs.rsov.test)
+
+rs_allocation g;
+
+int32_t RS_KERNEL getDim(int32_t dummy) {
+    return rsAllocationGetDimX(g);
+}
diff --git a/support/java/src/android/support/v8/renderscript/RenderScript.java b/support/java/src/android/support/v8/renderscript/RenderScript.java
index a5c6f93..19a33d7 100644
--- a/support/java/src/android/support/v8/renderscript/RenderScript.java
+++ b/support/java/src/android/support/v8/renderscript/RenderScript.java
@@ -1253,6 +1253,7 @@
         static final int RS_MESSAGE_TO_CLIENT_ERROR = 3;
 
         static final int RS_MESSAGE_TO_CLIENT_USER = 4;
+        static final int RS_ERROR_FATAL_DEBUG = 0x800;
         static final int RS_ERROR_FATAL_UNKNOWN = 0x1000;
 
         MessageThread(RenderScript rs) {
@@ -1295,7 +1296,19 @@
                 if (msg == RS_MESSAGE_TO_CLIENT_ERROR) {
                     String e = mRS.nContextGetErrorMessage(mRS.mContext);
 
-                    if (subID >= RS_ERROR_FATAL_UNKNOWN) {
+                    // Copied from java/android/renderscript/RenderScript.java
+                    // Throw RSRuntimeException under the following conditions:
+                    //
+                    // 1) It is an unknown fatal error.
+                    // 2) It is a debug fatal error, and we are not in a
+                    //    debug context.
+                    // 3) It is a debug fatal error, and we do not have an
+                    //    error callback.
+                    if (subID >= RS_ERROR_FATAL_UNKNOWN ||
+                        (subID >= RS_ERROR_FATAL_DEBUG &&
+                         (mRS.mContextType != ContextType.DEBUG ||
+                          mRS.mErrorCallback == null))) {
+                        android.util.Log.e(LOG_TAG, "fatal RS error, " + e);
                         throw new RSRuntimeException("Fatal error " + subID + ", details: " + e);
                     }
 
diff --git a/tests/java_api/RSTest_CompatLib/src/com/android/rs/test/apitest.rs b/tests/java_api/RSTest_CompatLib/src/com/android/rs/test/apitest.rs
index e7f8444..a39c12f 100644
--- a/tests/java_api/RSTest_CompatLib/src/com/android/rs/test/apitest.rs
+++ b/tests/java_api/RSTest_CompatLib/src/com/android/rs/test/apitest.rs
@@ -1349,11 +1349,13 @@
     _RS_ASSERT(rsAllocationGetDimZ(a) == z);
     _RS_ASSERT(rsAllocationGetDimLOD(a) == 0);
     _RS_ASSERT(rsAllocationGetDimFaces(a) == 0);
-
     rsSetElementAt_char(a, 5, 1, 0);
-    rsAllocationCopy1DRange(allocDst, 0, 0, x, a, 0, 0);
+    if (y > 0)
+        rsAllocationCopy2DRange(allocDst, 0, 0, 0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X,
+                                x, 1, a, 0, 0, 0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X);
+    else
+        rsAllocationCopy1DRange(allocDst, 0, 0, x, a, 0, 0);
     _RS_ASSERT(rsGetElementAt_char(allocDst, 1, 0) == 5);
-
     if (failed) {
         rsDebug("test_obj_api FAILED", -1);
     }