Initial backend compiler for RSoV
Bug: 30964317
This compiler compiles RenderScript LLVM bitcode into SPIR-V.
This was done by Jakub Kuderski as part of his 2016 summer internship
at Google.
Test: build
Change-Id: I395cdab2b97451b9e0a9b866af2d112ead73ab72
diff --git a/rsov/Android.mk b/rsov/Android.mk
new file mode 100644
index 0000000..2991304
--- /dev/null
+++ b/rsov/Android.mk
@@ -0,0 +1,8 @@
+#=====================================================================
+# Include Subdirectories
+#=====================================================================
+
+LOCAL_PATH:=$(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/rsov/compiler/Android.mk b/rsov/compiler/Android.mk
new file mode 100644
index 0000000..7f31d67
--- /dev/null
+++ b/rsov/compiler/Android.mk
@@ -0,0 +1,139 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+LLVM_ROOT_PATH := external/llvm
+LIBBCC_ROOT_PATH := frameworks/compile/libbcc
+LIBSPIRV_ROOT_PATH := external/spirv-llvm/lib/SPIRV
+
+FORCE_RS2SPIRV_DEBUG_BUILD ?= false
+RS2SPRIV_DEVICE_BUILD ?= true
+
+RS2SPIRV_SOURCES := \
+ rs2spirv.cpp \
+ GlobalMergePass.cpp \
+ InlinePreparationPass.cpp \
+ LinkerModule.cpp \
+ ReflectionPass.cpp \
+ RSAllocationUtils.cpp \
+ RSSPIRVWriter.cpp \
+ unit_tests/LinkerModuleTests.cpp
+
+RS2SPIRV_INCLUDES := \
+ $(LIBSPIRV_ROOT_PATH) \
+ $(LIBSPIRV_ROOT_PATH)/Mangler \
+ $(LIBSPIRV_ROOT_PATH)/libSPIRV \
+ $(LIBBCC_ROOT_PATH)/include \
+ $(LLVM_ROOT_PATH)/include \
+ $(LLVM_ROOT_PATH)/host/include
+
+#=====================================================================
+# Host Executable rs2spirv
+#=====================================================================
+
+# Don't build for unbundled branches
+ifeq (,$(TARGET_BUILD_APPS))
+
+include $(CLEAR_VARS)
+include $(CLEAR_TBLGEN_VARS)
+
+LOCAL_SRC_FILES := \
+ $(RS2SPIRV_SOURCES)
+
+LOCAL_C_INCLUDES := \
+ $(RS2SPIRV_INCLUDES)
+
+LOCAL_MODULE := rs2spirv
+LOCAL_MODULE_CLASS := EXECUTABLES
+
+# TODO: handle windows and darwin
+
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_IS_HOST_MODULE := true
+
+LOCAL_SHARED_LIBRARIES_linux += libLLVM libbcinfo libSPIRV
+
+# TODO: fix the remaining warnings
+
+LOCAL_CFLAGS += $(TOOL_CFLAGS) \
+ -D_SPIRV_LLVM_API \
+ -Wno-error=pessimizing-move \
+ -Wno-error=unused-variable \
+ -Wno-error=unused-private-field \
+ -Wno-error=unused-function \
+ -Wno-error=dangling-else \
+ -Wno-error=ignored-qualifiers \
+ -Wno-error=non-virtual-dtor
+
+ifeq (true, $(FORCE_RS2SPIRV_DEBUG_BUILD))
+ LOCAL_CFLAGS += -O0 -DRS2SPIRV_DEBUG=1
+endif
+
+include $(LLVM_ROOT_PATH)/llvm.mk
+include $(LLVM_GEN_INTRINSICS_MK)
+include $(LLVM_GEN_ATTRIBUTES_MK)
+include $(LLVM_HOST_BUILD_MK)
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # Don't build in unbundled branches
+
+#=====================================================================
+# Device Executable rs2spirv
+#=====================================================================
+
+ifneq (true,$(RS2SPRIV_DEVICE_BUILD)))
+
+include $(CLEAR_VARS)
+include $(CLEAR_TBLGEN_VARS)
+
+LOCAL_SRC_FILES := \
+ $(RS2SPIRV_SOURCES)
+
+LOCAL_C_INCLUDES := \
+ $(RS2SPIRV_INCLUDES)
+
+LOCAL_MODULE := rs2spirv
+LOCAL_MODULE_CLASS := EXECUTABLES
+
+LOCAL_SHARED_LIBRARIES += libLLVM libbcinfo libSPIRV
+
+LOCAL_CFLAGS += $(TOOL_CFLAGS) \
+ -D_SPIRV_LLVM_API \
+ -Wno-error=pessimizing-move \
+ -Wno-error=unused-variable \
+ -Wno-error=unused-private-field \
+ -Wno-error=unused-function \
+ -Wno-error=dangling-else \
+ -Wno-error=ignored-qualifiers \
+ -Wno-error=non-virtual-dtor \
+
+ifeq (true, $(FORCE_RS2SPIRV_DEBUG_BUILD))
+ LOCAL_CFLAGS += -O0 -DRS2SPIRV_DEBUG=1
+endif
+
+include $(LLVM_GEN_INTRINSICS_MK)
+include $(LLVM_GEN_ATTRIBUTES_MK)
+include $(LLVM_DEVICE_BUILD_MK)
+include $(BUILD_EXECUTABLE)
+
+endif # Don't build in unbundled branches
+
+#=====================================================================
+# Include Subdirectories
+#=====================================================================
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/rsov/compiler/GlobalMergePass.cpp b/rsov/compiler/GlobalMergePass.cpp
new file mode 100644
index 0000000..e177de8
--- /dev/null
+++ b/rsov/compiler/GlobalMergePass.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2016, 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 "GlobalMergePass.h"
+
+#include "RSAllocationUtils.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "rs2spirv-global-merge"
+
+using namespace llvm;
+
+namespace rs2spirv {
+
+namespace {
+
+class GlobalMergePass : public ModulePass {
+public:
+ static char ID;
+ GlobalMergePass() : ModulePass(ID) {}
+ const char *getPassName() const override { return "GlobalMergePass"; }
+
+ bool runOnModule(Module &M) override {
+ DEBUG(dbgs() << "RS2SPIRVGlobalMergePass\n");
+ DEBUG(M.dump());
+
+ const auto &DL = M.getDataLayout();
+ SmallVector<GlobalVariable *, 8> Globals;
+ const bool CollectRes = collectGlobals(M, Globals);
+ if (!CollectRes)
+ return false; // Module not modified.
+
+ IntegerType *Int32Ty = Type::getInt32Ty(M.getContext());
+ uint64_t MergedSize = 0;
+ SmallVector<Type *, 8> Tys;
+ Tys.reserve(Globals.size());
+
+ for (auto *GV : Globals) {
+ auto *Ty = GV->getValueType();
+ MergedSize += DL.getTypeAllocSize(Ty);
+ Tys.push_back(Ty);
+ }
+
+ auto *MergedTy = StructType::create(M.getContext(), "struct.__GPUBuffer");
+ MergedTy->setBody(Tys, false);
+ DEBUG(MergedTy->dump());
+ auto *MergedGV =
+ new GlobalVariable(M, MergedTy, false, GlobalValue::ExternalLinkage,
+ nullptr, "__GPUBlock");
+ MergedGV->setInitializer(nullptr); // TODO: Emit initializers for CPU code.
+
+ Value *Idx[2] = {ConstantInt::get(Int32Ty, 0), nullptr};
+
+ for (size_t i = 0, e = Globals.size(); i != e; ++i) {
+ auto *G = Globals[i];
+ Idx[1] = ConstantInt::get(Int32Ty, i);
+
+ // Keep users in a vector - they get implicitly removed
+ // in the loop below, which would invalidate users() iterators.
+ std::vector<User *> Users(G->user_begin(), G->user_end());
+ for (auto *User : Users) {
+ DEBUG(dbgs() << "User: ");
+ DEBUG(User->dump());
+ auto *Inst = dyn_cast<Instruction>(User);
+
+ // TODO: Consider what should actually happen. Global variables can
+ // appear in ConstantExprs, but this case requires fixing the LLVM-SPIRV
+ // converter, which currently emits ill-formed SPIR-V code.
+ if (!Inst) {
+ errs() << "Found a global variable user that is not an Instruction\n";
+ assert(false);
+ return true; // Module may have been modified.
+ }
+
+ auto *GEP = GetElementPtrInst::CreateInBounds(MergedTy, MergedGV, Idx,
+ "gpu_gep", Inst);
+ for (unsigned k = 0, k_e = User->getNumOperands(); k != k_e; ++k)
+ if (User->getOperand(k) == G)
+ User->setOperand(k, GEP);
+ }
+
+ // TODO: Investigate emitting a GlobalAlias for each global variable.
+ G->eraseFromParent();
+ }
+
+ // Return true, as the pass modifies module.
+ return true;
+ }
+
+private:
+ bool collectGlobals(Module &M, SmallVectorImpl<GlobalVariable *> &Globals) {
+ for (auto &GV : M.globals()) {
+ // TODO: Rethink what should happen with global statics.
+ if (GV.isDeclaration() || GV.isThreadLocal() || GV.hasSection())
+ continue;
+
+ if (isRSAllocation(GV))
+ continue;
+
+ DEBUG(GV.dump());
+ auto *PT = cast<PointerType>(GV.getType());
+
+ const unsigned AddressSpace = PT->getAddressSpace();
+ if (AddressSpace != 0) {
+ errs() << "Unknown address space! (" << AddressSpace
+ << ")\nGlobalMergePass failed!\n";
+ return false;
+ }
+
+ Globals.push_back(&GV);
+ }
+
+ return !Globals.empty();
+ }
+};
+}
+
+char GlobalMergePass::ID = 0;
+
+ModulePass *createGlobalMergePass() { return new GlobalMergePass(); }
+
+} // namespace rs2spirv
diff --git a/rsov/compiler/GlobalMergePass.h b/rsov/compiler/GlobalMergePass.h
new file mode 100644
index 0000000..c402e59
--- /dev/null
+++ b/rsov/compiler/GlobalMergePass.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016, 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_GLOBAL_MERGE_PASS_H
+#define RS2SPIRV_GLOBAL_MERGE_PASS_H
+
+namespace llvm {
+class ModulePass;
+}
+
+namespace rs2spirv {
+
+llvm::ModulePass *createGlobalMergePass();
+
+} // namespace rs2spirv
+
+#endif
diff --git a/rsov/compiler/InlinePreparationPass.cpp b/rsov/compiler/InlinePreparationPass.cpp
new file mode 100644
index 0000000..9e016fa
--- /dev/null
+++ b/rsov/compiler/InlinePreparationPass.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016, 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 "InlinePreparationPass.h"
+
+#include "bcinfo/MetadataExtractor.h"
+
+#include "llvm/ADT/StringSet.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "rs2spirv-inline"
+
+using namespace llvm;
+
+namespace rs2spirv {
+
+namespace {
+
+class InlinePreparationPass : public ModulePass {
+ bcinfo::MetadataExtractor &ME;
+
+public:
+ static char ID;
+ explicit InlinePreparationPass(bcinfo::MetadataExtractor &Extractor)
+ : ModulePass(ID), ME(Extractor) {}
+
+ 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]);
+
+ for (auto &F : M.functions()) {
+ 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)
+ continue; // Skip kernels.
+
+ F.addFnAttr(Attribute::AlwaysInline);
+ F.setLinkage(GlobalValue::InternalLinkage);
+ DEBUG(dbgs() << "Marked as alwaysinline:\t" << FName << '\n');
+ }
+
+ // Return true, as the pass modifies module.
+ return true;
+ }
+};
+}
+
+char InlinePreparationPass::ID = 0;
+
+ModulePass *createInlinePreparationPass(bcinfo::MetadataExtractor &ME) {
+ return new InlinePreparationPass(ME);
+}
+
+} // namespace rs2spirv
diff --git a/rsov/compiler/InlinePreparationPass.h b/rsov/compiler/InlinePreparationPass.h
new file mode 100644
index 0000000..28f2a74
--- /dev/null
+++ b/rsov/compiler/InlinePreparationPass.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016, 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_INLINE_PREPARATION_PASS_H
+#define RS2SPIRV_INLINE_PREPARATION_PASS_H
+
+namespace llvm {
+class ModulePass;
+}
+
+namespace bcinfo {
+class MetadataExtractor;
+}
+
+namespace rs2spirv {
+
+llvm::ModulePass *
+createInlinePreparationPass(bcinfo::MetadataExtractor &Extractor);
+
+} // namespace rs2spirv
+
+#endif
diff --git a/rsov/compiler/LinkerModule.cpp b/rsov/compiler/LinkerModule.cpp
new file mode 100644
index 0000000..438af09
--- /dev/null
+++ b/rsov/compiler/LinkerModule.cpp
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2016, 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 "LinkerModule.h"
+
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <fstream>
+#include <sstream>
+
+#define DEBUG_TYPE "rs2spirv-module"
+
+using namespace llvm;
+
+namespace rs2spirv {
+
+bool SPIRVLine::hasCode() const {
+ StringRef S(Line);
+ S = S.trim();
+ if (S.empty())
+ return false;
+ if (S[0] == ';')
+ return false;
+
+ return true;
+}
+
+static Optional<StringRef> GetFirstId(StringRef S, size_t StartPos,
+ size_t &EndPos) {
+ size_t Pos = S.find('%', StartPos);
+ if (Pos == StringRef::npos) {
+ return None;
+ }
+
+ const auto PosB = Pos;
+ while (++Pos < S.size() && isspace(S[Pos]) == 0)
+ ;
+
+ EndPos = Pos;
+ return StringRef(S.data() + PosB, EndPos - PosB);
+}
+
+void SPIRVLine::getIdentifiers(SmallVectorImpl<StringRef> &Out,
+ size_t StartPos) const {
+ const StringRef S(Line);
+
+ size_t Pos = StartPos;
+ Optional<StringRef> Res;
+ while ((Res = GetFirstId(S, Pos, Pos)))
+ Out.push_back(*Res);
+}
+
+Optional<StringRef> SPIRVLine::getLHSIdentifier() const {
+ size_t EndPos;
+ const auto Id = GetFirstId(Line, 0, EndPos);
+ if (!Id)
+ return None;
+
+ if (!contains("="))
+ return None;
+
+ return *Id;
+}
+
+Optional<StringRef> SPIRVLine::getRHS() const {
+ const auto EqPos = Line.find('=', 0);
+ if (EqPos == std::string::npos)
+ return None;
+
+ return StringRef(Line.c_str() + EqPos + 1).trim();
+}
+
+void SPIRVLine::getRHSIdentifiers(SmallVectorImpl<StringRef> &Out) const {
+ const auto RHS = getRHS();
+ if (!RHS)
+ return;
+
+ size_t Pos = 0;
+ Optional<StringRef> Res;
+ while ((Res = GetFirstId(*RHS, Pos, Pos)))
+ Out.push_back(*Res);
+}
+
+bool SPIRVLine::replaceStr(StringRef Original, StringRef New) {
+ const size_t Pos = StringRef(Line).find(Original);
+ if (Pos == StringRef::npos)
+ return false;
+
+ Line.replace(Pos, Original.size(), New.str());
+ return true;
+}
+
+bool SPIRVLine::replaceId(StringRef Original, StringRef New) {
+ size_t Pos = StringRef(Line).find(Original, 0);
+ if (Pos == StringRef::npos)
+ return false;
+
+ const auto OneAfter = Pos + Original.size();
+ if (OneAfter < Line.size() && isspace(Line[OneAfter]) == 0) {
+ Pos = StringRef(Line).find(Original, OneAfter);
+ if (Pos == StringRef::npos)
+ return false;
+ }
+
+ Line.replace(Pos, Original.size(), New.str());
+ return true;
+}
+
+bool SPIRVLine::contains(StringRef S) const {
+ return StringRef(Line).find(S, 0) != StringRef::npos;
+}
+
+void SPIRVLine::markAsEmpty() { Line = "; <<empty>>"; }
+
+Block &Block::operator=(const Block &B) {
+ assert(Kind == B.Kind);
+ assert(Name == B.Name);
+ Lines = B.Lines;
+
+ return *this;
+}
+
+bool Block::addLine(SPIRVLine L, bool trim) {
+ if (trim)
+ L.trim();
+
+ Lines.emplace_back(std::move(L));
+ return true;
+}
+
+SPIRVLine &Block::getLastLine() {
+ assert(!Lines.empty());
+ return Lines.back();
+}
+
+const SPIRVLine &Block::getLastLine() const {
+ assert(!Lines.empty());
+ return Lines.back();
+}
+
+void Block::appendToStream(std::ostream &OS) const {
+ for (const auto &L : Lines)
+ OS << L.str() << '\n';
+}
+
+void Block::dump() const {
+ dbgs() << "\n" << Name << "Block: {\n\n";
+ for (const auto &L : Lines) {
+ if (L.hasCode())
+ dbgs() << '\t';
+ dbgs() << L.str() << '\n';
+ }
+ dbgs() << "\n} (" << Name << "Block)\n\n";
+}
+
+void Block::replaceAllIds(StringRef Old, StringRef New) {
+ for (auto &L : Lines)
+ while (L.replaceId(Old, New))
+ ;
+}
+
+bool Block::hasCode() const {
+ return std::any_of(Lines.begin(), Lines.end(),
+ [](const SPIRVLine &L) { return L.hasCode(); });
+}
+
+size_t Block::getIdCount(StringRef Id) const {
+ size_t Res = 0;
+ for (const auto &L : Lines) {
+ SmallVector<StringRef, 4> Ids;
+ L.getIdentifiers(Ids);
+ Res += std::count(Ids.begin(), Ids.end(), Id);
+ }
+
+ return Res;
+}
+
+void Block::removeNonCodeLines() {
+ Lines.erase(std::remove_if(Lines.begin(), Lines.end(),
+ [](const SPIRVLine &L) { return !L.hasCode(); }),
+ Lines.end());
+}
+
+bool HeaderBlock::getRSKernelNames(SmallVectorImpl<StringRef> &Out) const {
+ for (const auto &L : Lines)
+ if (L.contains("OpString")) {
+ const Optional<StringRef> Name = L.getLHSIdentifier();
+ if (Name && *Name == "%RS_KERNELS") {
+ auto LStr = L.str();
+ LStr.erase(std::remove(LStr.begin(), LStr.end(), '"'), LStr.end());
+
+ SPIRVLine(LStr).getRHSIdentifiers(Out);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+StringRef FunctionBlock::getFunctionName() const {
+ assert(!Lines.empty());
+ assert(Lines.front().contains("OpFunction"));
+
+ Optional<StringRef> Name = Lines.front().getLHSIdentifier();
+ assert(Name);
+ return *Name;
+}
+
+size_t FunctionBlock::getArity() const {
+ size_t A = 0;
+ for (const auto &L : Lines)
+ if (L.contains("OpFunctionParameter"))
+ ++A;
+
+ return A;
+}
+
+void FunctionBlock::getArgNames(SmallVectorImpl<StringRef> &Out) const {
+ for (const auto &L : Lines)
+ if (L.contains("OpFunctionParameter")) {
+ Optional<StringRef> Id = L.getLHSIdentifier();
+ assert(Id);
+ Out.push_back(*Id);
+ }
+}
+
+Optional<StringRef> FunctionBlock::getRetValName() const {
+ for (const auto &L : Lines)
+ if (L.contains("OpReturnValue")) {
+ SmallVector<StringRef, 1> Id;
+ L.getIdentifiers(Id);
+ assert(Id.size() == 1);
+ return Id.front();
+ }
+
+ return None;
+}
+
+iterator_range<Block::const_line_iter> FunctionBlock::body() const {
+ auto It = Lines.begin();
+ const auto End = Lines.end();
+
+ while (It != End && !It->contains("OpLabel"))
+ ++It;
+
+ assert(It != End);
+
+ ++It;
+ const auto BBegin = It;
+
+ while (It != End && !It->contains("OpReturn"))
+ ++It;
+
+ assert(It != End);
+
+ return make_range(BBegin, It);
+}
+
+void FunctionBlock::getCalledFunctions(
+ llvm::SmallVectorImpl<llvm::StringRef> &Out) const {
+ for (const auto &L : Lines)
+ if (L.contains("OpFunctionCall")) {
+ SmallVector<StringRef, 4> Ids;
+ L.getRHSIdentifiers(Ids);
+ assert(Ids.size() >= 2);
+
+ Out.push_back(Ids[1]);
+ }
+}
+
+bool FunctionBlock::hasFunctionCalls() const {
+ SmallVector<StringRef, 4> Callees;
+ getCalledFunctions(Callees);
+ return !Callees.empty();
+}
+
+bool FunctionBlock::isDirectlyRecursive() const {
+ SmallVector<StringRef, 4> Callees;
+ getCalledFunctions(Callees);
+
+ const auto FName = getFunctionName();
+ return std::find(Callees.begin(), Callees.end(), FName) != Callees.end();
+}
+
+bool FunctionBlock::isReturnTypeVoid() const {
+ assert(Lines.size() >= 4);
+ // At least 4 lines: OpFunction, OpLabel, OpReturn, OpFunctionEnd.
+
+ SmallVector<StringRef, 2> Ids;
+ Lines.front().getRHSIdentifiers(Ids);
+ assert(Ids.size() == 2);
+
+ if (Ids.front() != "%void" && Ids.front() != "%rs_linker_void")
+ return false;
+
+ SPIRVLine SecondLast = Lines[Lines.size() - 2];
+ SecondLast.trim();
+ return SecondLast.str() == "OpReturn";
+}
+
+LinkerModule::LinkerModule(std::istream &ModuleIn) {
+ std::string Temp;
+ std::vector<SPIRVLine> Ls;
+ while (std::getline(ModuleIn, Temp))
+ Ls.push_back(StringRef(Temp));
+
+ auto It = Ls.begin();
+ const auto End = Ls.end();
+
+ {
+ auto &HeaderBlck = addBlock<HeaderBlock>();
+ while (It != End && !It->contains("OpDecorate"))
+ HeaderBlck.addLine(*(It++));
+ }
+
+ {
+ auto &DcrBlck = addBlock<DecorBlock>();
+ while (It != End && !It->contains("OpType"))
+ DcrBlck.addLine(*(It++));
+
+ DcrBlck.removeNonCodeLines();
+ }
+
+ {
+ auto &TypeAndConstBlck = addBlock<TypeAndConstBlock>();
+ auto &VarBlck = addBlock<VarBlock>();
+
+ while (It != End && !It->contains("OpFunction")) {
+ if (!It->hasCode()) {
+ ++It;
+ continue;
+ }
+
+ if (It->contains("OpType") || It->contains("OpConstant")) {
+ TypeAndConstBlck.addLine(*It);
+ } else {
+ VarBlck.addLine(*It);
+ }
+
+ ++It;
+ }
+
+ TypeAndConstBlck.removeNonCodeLines();
+ VarBlck.removeNonCodeLines();
+ }
+
+ while (It != End) {
+ // Consume empty lines between blocks.
+ if (It->empty()) {
+ ++It;
+ continue;
+ }
+
+ Optional<StringRef> Id = It->getLHSIdentifier();
+ assert(Id && "Functions should start with OpFunction");
+
+ FunctionBlock &FunBlck =
+ *Id == "%main" ? addBlock<MainFunBlock>() : addBlock<FunctionBlock>();
+ bool HasReturn = false;
+
+ while (It != End) {
+ if (It->empty()) {
+ ++It;
+ continue;
+ }
+ HasReturn |= It->contains("OpReturn");
+
+ FunBlck.addLine(*(It++));
+ if (FunBlck.getLastLine().contains("OpFunctionEnd"))
+ break;
+ }
+
+ FunBlck.removeNonCodeLines();
+
+ if (!HasReturn) {
+ FunDeclBlock FunDeclBlck;
+ for (auto &L : FunBlck.lines())
+ FunDeclBlck.addLine(std::move(L));
+
+ Blocks.pop_back();
+ addBlock<FunDeclBlock>(std::move(FunDeclBlck));
+ }
+ }
+
+ removeNonCode();
+}
+
+void LinkerModule::fixBlockOrder() {
+ std::stable_sort(Blocks.begin(), Blocks.end(),
+ [](const block_ptr &LHS, const block_ptr &RHS) {
+ return LHS->getKind() < RHS->getKind();
+ });
+}
+
+bool LinkerModule::saveToFile(StringRef FName) const {
+ std::ofstream Out(FName, std::ios::trunc);
+ if (!Out.good())
+ return false;
+
+ for (const auto &BPtr : blocks()) {
+ if (!isa<HeaderBlock>(BPtr.get()))
+ Out << "\n\n; " << BPtr->Name.str() << "\n\n";
+
+ for (const auto &L : BPtr->lines()) {
+ if (L.hasCode())
+ Out << "\t";
+ Out << L.str() << '\n';
+ }
+ }
+
+ return true;
+}
+
+void LinkerModule::removeEmptyBlocks() {
+ removeBlocksIf([](const Block &B) { return B.empty(); });
+}
+
+void LinkerModule::removeNonCode() {
+ for (auto &BPtr : Blocks)
+ if (!isa<HeaderBlock>(BPtr.get()))
+ BPtr->removeNonCodeLines();
+
+ removeBlocksIf([](const Block &B) { return !B.hasCode(); });
+}
+
+void LinkerModule::removeUnusedFunctions() {
+ std::vector<std::string> UsedFunctions;
+
+ assert(Blocks.size());
+
+ const auto &MB = getLastBlock<MainFunBlock>();
+ for (const auto &L : MB.lines())
+ if (L.contains("OpFunctionCall")) {
+ SmallVector<StringRef, 4> Ids;
+ L.getRHSIdentifiers(Ids);
+ assert(Ids.size() >= 2);
+
+ const auto &FName = Ids[1];
+ UsedFunctions.push_back(FName.str());
+ }
+
+ removeBlocksIf([&UsedFunctions](const Block &B) {
+ const auto *FunBlck = dyn_cast<FunctionBlock>(&B);
+ if (!FunBlck)
+ return false;
+
+ if (isa<MainFunBlock>(FunBlck))
+ return false;
+
+ const auto FName = FunBlck->getFunctionName().str();
+ return std::find(UsedFunctions.begin(), UsedFunctions.end(), FName) ==
+ UsedFunctions.end();
+ });
+}
+
+} // namespace rs2spirv
diff --git a/rsov/compiler/LinkerModule.h b/rsov/compiler/LinkerModule.h
new file mode 100644
index 0000000..ed29153
--- /dev/null
+++ b/rsov/compiler/LinkerModule.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2016, 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 RS_LINKER_MODULE_H
+#define RS_LINKER_MODULE_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
+
+#include <algorithm>
+#include <cassert>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+namespace rs2spirv {
+
+class SPIRVLine {
+ std::string Line;
+
+public:
+ SPIRVLine(llvm::StringRef L) : Line(L.str()) {}
+
+ std::string &str() { return Line; }
+ const std::string &str() const { return Line; }
+
+ void trim() { Line = llvm::StringRef(Line).trim().str(); }
+
+ bool empty() const { return Line.empty(); }
+ bool hasCode() const;
+
+ void getIdentifiers(llvm::SmallVectorImpl<llvm::StringRef> &Out,
+ size_t StartPos = 0) const;
+ llvm::Optional<llvm::StringRef> getLHSIdentifier() const;
+ void getRHSIdentifiers(llvm::SmallVectorImpl<llvm::StringRef> &Out) const;
+ llvm::Optional<llvm::StringRef> getRHS() const;
+ bool replaceId(llvm::StringRef Original, llvm::StringRef New);
+ bool replaceStr(llvm::StringRef Original, llvm::StringRef New);
+ bool contains(llvm::StringRef S) const;
+
+ void markAsEmpty();
+};
+
+class Block {
+public:
+ enum BlockKind {
+ BK_Header,
+ BK_Decor,
+ BK_TypeAndConst,
+ BK_Var,
+ BK_FunDecl,
+ BK_Function,
+ BK_MainFun
+ };
+
+private:
+ const BlockKind Kind;
+
+protected:
+ llvm::SmallVector<SPIRVLine, 4> Lines;
+
+public:
+ using line_iter = decltype(Lines)::iterator;
+ using const_line_iter = decltype(Lines)::const_iterator;
+
+ const llvm::StringRef Name;
+
+ BlockKind getKind() const { return Kind; }
+
+ Block(BlockKind K, llvm::StringRef N) : Kind(K), Name(N) {}
+ Block(const Block &) = default;
+ Block &operator=(const Block &B);
+
+ virtual ~Block() = default;
+
+ virtual bool addLine(SPIRVLine L, bool trim = true);
+
+ llvm::iterator_range<line_iter> lines() {
+ return llvm::make_range(Lines.begin(), Lines.end());
+ }
+
+ llvm::iterator_range<const_line_iter> lines() const {
+ return llvm::make_range(Lines.begin(), Lines.end());
+ }
+
+ size_t getNumLines() const { return Lines.size(); }
+ bool empty() const { return Lines.empty(); }
+ bool hasCode() const;
+ size_t getIdCount(llvm::StringRef Id) const;
+ virtual void replaceAllIds(llvm::StringRef Old, llvm::StringRef New);
+ SPIRVLine &getLastLine();
+ const SPIRVLine &getLastLine() const;
+ virtual void appendToStream(std::ostream &OS) const;
+ void removeNonCodeLines();
+
+ virtual void dump() const;
+};
+
+class HeaderBlock : public Block {
+public:
+ HeaderBlock() : Block(BK_Header, "Header") {}
+ HeaderBlock(const HeaderBlock &) = default;
+ HeaderBlock &operator=(const HeaderBlock &) = default;
+ static bool classof(const Block *B) { return B->getKind() == BK_Header; }
+
+ bool getRSKernelNames(llvm::SmallVectorImpl<llvm::StringRef> &Out) const;
+};
+
+class DecorBlock : public Block {
+public:
+ DecorBlock() : Block(BK_Decor, "Decor") {}
+ DecorBlock(const DecorBlock &) = default;
+ DecorBlock &operator=(const DecorBlock &) = default;
+ static bool classof(const Block *B) { return B->getKind() == BK_Decor; }
+};
+
+// Block containing (interleaved) type and constant definitions.
+class TypeAndConstBlock : public Block {
+public:
+ TypeAndConstBlock() : Block(BK_TypeAndConst, "TypeAndConst") {}
+ TypeAndConstBlock(const TypeAndConstBlock &) = default;
+ TypeAndConstBlock &operator=(const TypeAndConstBlock &) = default;
+ static bool classof(const Block *B) {
+ return B->getKind() == BK_TypeAndConst;
+ }
+};
+
+class VarBlock : public Block {
+public:
+ VarBlock() : Block(BK_Var, "Var") {}
+ VarBlock(const VarBlock &) = default;
+ VarBlock &operator=(const VarBlock &) = default;
+ static bool classof(const Block *B) { return B->getKind() == BK_Var; }
+};
+
+class FunDeclBlock : public Block {
+public:
+ FunDeclBlock() : Block(BK_FunDecl, "FunDecl") {}
+ FunDeclBlock(const FunDeclBlock &) = default;
+ FunDeclBlock &operator=(const FunDeclBlock &) = default;
+ static bool classof(const Block *B) { return B->getKind() == BK_FunDecl; }
+};
+
+class FunctionBlock : public Block {
+public:
+ FunctionBlock() : Block(BK_Function, "Function") {}
+ FunctionBlock(const FunctionBlock &) = default;
+ FunctionBlock &operator=(const FunctionBlock &) = default;
+ static bool classof(const Block *B) {
+ return B->getKind() >= BK_Function && B->getKind() <= BK_MainFun;
+ }
+
+ llvm::StringRef getFunctionName() const;
+ size_t getArity() const;
+ void getArgNames(llvm::SmallVectorImpl<llvm::StringRef> &Out) const;
+ llvm::Optional<llvm::StringRef> getRetValName() const;
+ llvm::iterator_range<const_line_iter> body() const;
+ void getCalledFunctions(llvm::SmallVectorImpl<llvm::StringRef> &Out) const;
+ bool hasFunctionCalls() const;
+ bool isDirectlyRecursive() const;
+ bool isReturnTypeVoid() const;
+
+protected:
+ FunctionBlock(BlockKind BK, llvm::StringRef N) : Block(BK, N) {}
+};
+
+class MainFunBlock : public FunctionBlock {
+public:
+ MainFunBlock() : FunctionBlock(BK_MainFun, "MainFun") {}
+ MainFunBlock(const MainFunBlock &) = default;
+ MainFunBlock &operator=(const MainFunBlock &) = default;
+ static bool classof(const Block *B) { return B->getKind() == BK_MainFun; }
+};
+
+class LinkerModule {
+ using block_ptr = std::unique_ptr<Block>;
+ std::vector<block_ptr> Blocks;
+
+public:
+ using block_iter = decltype(Blocks)::iterator;
+ using const_block_iter = decltype(Blocks)::const_iterator;
+
+ LinkerModule(std::istream &ModuleIn);
+ LinkerModule() = default;
+
+ void dump() const {
+ for (const auto &Blck : Blocks)
+ Blck->dump();
+ }
+
+ std::vector<SPIRVLine *> lines() {
+ std::vector<SPIRVLine *> res;
+ for (auto &B : Blocks)
+ for (auto &L : B->lines())
+ res.emplace_back(&L);
+
+ return res;
+ }
+
+ std::vector<const SPIRVLine *> lines() const {
+ std::vector<const SPIRVLine *> res;
+ for (const auto &B : Blocks)
+ for (const auto &L : B->lines())
+ res.emplace_back(&L);
+
+ return res;
+ }
+
+ llvm::iterator_range<block_iter> blocks() {
+ return llvm::make_range(Blocks.begin(), Blocks.end());
+ }
+
+ llvm::iterator_range<const_block_iter> blocks() const {
+ return llvm::make_range(Blocks.cbegin(), Blocks.cend());
+ }
+
+ template <typename T, typename... Ts> T &addBlock(Ts &&... ts) {
+ Blocks.emplace_back(std::unique_ptr<T>(new T(std::forward<Ts>(ts)...)));
+ return *llvm::cast<T>(Blocks.back().get());
+ }
+
+ template <typename T> T &getLastBlock() {
+ assert(Blocks.size());
+ return *llvm::cast<T>(Blocks.back().get());
+ }
+
+ template <typename T> const T &getLastBlock() const {
+ assert(Blocks.size());
+ return *llvm::cast<T>(Blocks.back().get());
+ }
+
+ template <typename P>
+ void getBlocksIf(llvm::SmallVectorImpl<Block *> &Out, P Predicate) {
+ for (auto &BPtr : Blocks)
+ if (Predicate(*BPtr))
+ Out.push_back(BPtr.get());
+ }
+
+ template <typename P>
+ void getBlocksIf(llvm::SmallVectorImpl<const Block *> &Out,
+ P Predicate) const {
+ for (const auto &BPtr : Blocks)
+ if (Predicate(*BPtr))
+ Out.push_back(BPtr.get());
+ }
+
+ template <typename P> void removeBlocksIf(P Predicate) {
+ Blocks.erase(std::remove_if(Blocks.begin(), Blocks.end(),
+ [&Predicate](const block_ptr &BPtr) {
+ return Predicate(*BPtr);
+ }),
+ Blocks.end());
+ }
+
+ void fixBlockOrder();
+ bool saveToFile(llvm::StringRef FName) const;
+ void removeEmptyBlocks();
+ void removeNonCode();
+ void removeUnusedFunctions();
+};
+
+} // namespace rs2spirv
+
+#endif
\ No newline at end of file
diff --git a/rsov/compiler/RSAllocationUtils.cpp b/rsov/compiler/RSAllocationUtils.cpp
new file mode 100644
index 0000000..dbcb2a8
--- /dev/null
+++ b/rsov/compiler/RSAllocationUtils.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2016, 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 "RSAllocationUtils.h"
+
+#include "SPIRVInternal.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+#include <algorithm>
+#include <sstream>
+#include <unordered_map>
+
+#define DEBUG_TYPE "rs2spirv-rs-allocation-utils"
+
+using namespace llvm;
+
+namespace rs2spirv {
+
+bool isRSAllocation(const GlobalVariable &GV) {
+ auto *PT = cast<PointerType>(GV.getType());
+ DEBUG(PT->dump());
+
+ auto *VT = PT->getElementType();
+ DEBUG(VT->dump());
+ std::string TypeName;
+ raw_string_ostream RSO(TypeName);
+ VT->print(RSO);
+ RSO.str(); // Force flush.
+ DEBUG(dbgs() << "TypeName: " << TypeName << '\n');
+
+ return TypeName.find("struct.rs_allocation") != std::string::npos;
+}
+
+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});
+ }
+
+ return true;
+}
+
+bool getRSAllocAccesses(SmallVectorImpl<RSAllocationInfo> &Allocs,
+ SmallVectorImpl<RSAllocationCallInfo> &Calls) {
+ DEBUG(dbgs() << "getRSGEATCalls\n");
+ DEBUG(dbgs() << "\n\n~~~~~~~~~~~~~~~~~~~~~\n\n");
+
+ std::unordered_map<Value *, GlobalVariable *> Mapping;
+
+ for (auto &A : Allocs) {
+ auto *GV = A.GlobalVar;
+ std::vector<User *> WorkList(GV->user_begin(), GV->user_end());
+ size_t Idx = 0;
+
+ while (Idx < WorkList.size()) {
+ auto *U = WorkList[Idx];
+ DEBUG(dbgs() << "Visiting ");
+ DEBUG(U->dump());
+ ++Idx;
+ auto It = Mapping.find(U);
+ if (It != Mapping.end()) {
+ if (It->second == GV) {
+ continue;
+ } else {
+ errs() << "Duplicate global mapping discovered!\n";
+ errs() << "\nGlobal: ";
+ GV->print(errs());
+ errs() << "\nExisting mapping: ";
+ It->second->print(errs());
+ errs() << "\nUser: ";
+ U->print(errs());
+ errs() << '\n';
+
+ return false;
+ }
+ }
+
+ Mapping[U] = GV;
+ DEBUG(dbgs() << "New mapping: ");
+ DEBUG(U->print(dbgs()));
+ DEBUG(dbgs() << " -> " << GV->getName() << '\n');
+
+ if (auto *FCall = dyn_cast<CallInst>(U)) {
+ if (auto *F = FCall->getCalledFunction()) {
+ const auto FName = F->getName();
+ DEBUG(dbgs() << "Discovered function call to : " << FName << '\n');
+
+ std::string DemangledName;
+ oclIsBuiltin(FName, &DemangledName);
+ const StringRef DemangledNameRef(DemangledName);
+ DEBUG(dbgs() << "Demangled name: " << DemangledNameRef << '\n');
+
+ const StringRef GEAPrefix = "rsGetElementAt_";
+ const StringRef SEAPrefix = "rsSetElementAt_";
+ assert(GEAPrefix.size() == SEAPrefix.size());
+
+ const bool IsGEA = DemangledNameRef.startswith(GEAPrefix);
+ const bool IsSEA = DemangledNameRef.startswith(SEAPrefix);
+
+ assert(!IsGEA || !IsSEA);
+
+ if (IsGEA || IsSEA) {
+ DEBUG(dbgs() << "Found rsAlloc function!\n");
+
+ const auto Kind =
+ IsGEA ? RSAllocAccessKind::GEA : RSAllocAccessKind::SEA;
+
+ const auto RSElementTy =
+ DemangledNameRef.drop_front(GEAPrefix.size());
+
+ Calls.push_back({A, FCall, Kind, RSElementTy.str()});
+ continue;
+ } else if (DemangledNameRef.startswith(GEAPrefix.drop_back()) ||
+ DemangledNameRef.startswith(SEAPrefix.drop_back())) {
+ errs() << "Untyped accesses to global rs_allocations are not "
+ "supported.\n";
+ return false;
+ }
+ }
+ }
+
+ // TODO: Consider using set-like container to reduce computational
+ // complexity.
+ for (auto *NewU : U->users())
+ if (std::find(WorkList.begin(), WorkList.end(), NewU) == WorkList.end())
+ WorkList.push_back(NewU);
+ }
+ }
+
+ std::unordered_map<GlobalVariable *, std::string> GVAccessTypes;
+
+ for (auto &Access : Calls) {
+ auto AccessElemTyIt = GVAccessTypes.find(Access.RSAlloc.GlobalVar);
+ if (AccessElemTyIt != GVAccessTypes.end() &&
+ AccessElemTyIt->second != Access.RSElementTy) {
+ errs() << "Could not infere element type for: ";
+ Access.RSAlloc.GlobalVar->print(errs());
+ errs() << '\n';
+ return false;
+ } else if (AccessElemTyIt == GVAccessTypes.end()) {
+ GVAccessTypes.emplace(Access.RSAlloc.GlobalVar, Access.RSElementTy);
+ Access.RSAlloc.RSElementType = Access.RSElementTy;
+ }
+ }
+
+ DEBUG(dbgs() << "\n\n~~~~~~~~~~~~~~~~~~~~~\n\n");
+ return true;
+}
+
+bool solidifyRSAllocAccess(Module &M, RSAllocationCallInfo CallInfo) {
+ DEBUG(dbgs() << "\tsolidifyRSAllocAccess " << CallInfo.RSAlloc.VarName
+ << '\n');
+ auto *FCall = CallInfo.FCall;
+ auto *Fun = FCall->getCalledFunction();
+ assert(Fun);
+
+ const auto FName = Fun->getName();
+
+ StringRef GVName = CallInfo.RSAlloc.VarName;
+ std::ostringstream OSS;
+ OSS << "RS_" << GVName.drop_front().str() << FName.str();
+
+ auto *NewF = Function::Create(Fun->getFunctionType(),
+ Function::ExternalLinkage, OSS.str(), &M);
+ FCall->setCalledFunction(NewF);
+ NewF->setAttributes(Fun->getAttributes());
+
+ DEBUG(M.dump());
+
+ return true;
+}
+
+} // rs2spirv
diff --git a/rsov/compiler/RSAllocationUtils.h b/rsov/compiler/RSAllocationUtils.h
new file mode 100644
index 0000000..5b5b818
--- /dev/null
+++ b/rsov/compiler/RSAllocationUtils.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016, 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_RS_ALLOCATION_UTILS_H
+#define RS2SPIRV_RS_ALLOCATION_UTILS_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace llvm {
+class CallInst;
+class GlobalVariable;
+class Module;
+class Type;
+}
+
+namespace rs2spirv {
+
+struct RSAllocationInfo {
+ std::string VarName;
+ llvm::Optional<std::string> RSElementType;
+ llvm::GlobalVariable *GlobalVar;
+};
+
+enum class RSAllocAccessKind { GEA, SEA };
+
+struct RSAllocationCallInfo {
+ RSAllocationInfo &RSAlloc;
+ llvm::CallInst *FCall;
+ RSAllocAccessKind Kind;
+ std::string RSElementTy;
+};
+
+bool isRSAllocation(const llvm::GlobalVariable &GV);
+llvm::Optional<llvm::StringRef> getRSTypeName(const llvm::GlobalVariable &GV);
+bool getRSAllocationInfo(llvm::Module &M,
+ llvm::SmallVectorImpl<RSAllocationInfo> &Allocs);
+bool getRSAllocAccesses(llvm::SmallVectorImpl<RSAllocationInfo> &Allocs,
+ llvm::SmallVectorImpl<RSAllocationCallInfo> &Calls);
+bool solidifyRSAllocAccess(llvm::Module &M, RSAllocationCallInfo CallInfo);
+
+} // namespace rs2spirv
+
+#endif
diff --git a/rsov/compiler/RSSPIRVWriter.cpp b/rsov/compiler/RSSPIRVWriter.cpp
new file mode 100644
index 0000000..39632c9
--- /dev/null
+++ b/rsov/compiler/RSSPIRVWriter.cpp
@@ -0,0 +1,603 @@
+/*
+ * Copyright 2016, 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 "RSSPIRVWriter.h"
+
+#include "SPIRVModule.h"
+#include "bcinfo/MetadataExtractor.h"
+
+#include "llvm/ADT/StringMap.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 "GlobalMergePass.h"
+#include "LinkerModule.h"
+#include "ReflectionPass.h"
+#include "InlinePreparationPass.h"
+
+#include <fstream>
+#include <sstream>
+
+#define DEBUG_TYPE "rs2spirv-writer"
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+FunctionPass *createPromoteMemoryToRegisterPass();
+}
+
+namespace rs2spirv {
+
+static cl::opt<std::string> WrapperOutputFile("wo",
+ cl::desc("Wrapper output file"),
+ cl::value_desc("filename.spt"));
+
+static bool FixMain(LinkerModule &LM, MainFunBlock &MB, StringRef KernelName);
+static bool InlineFunctionCalls(LinkerModule &LM, MainFunBlock &MB);
+static bool FuseTypesAndConstants(LinkerModule &LM);
+static bool TranslateInBoundsPtrAccessToAccess(SPIRVLine &L);
+static bool FixVectorShuffles(MainFunBlock &MB);
+static void FixModuleStorageClass(LinkerModule &M);
+
+static void HandleTargetTriple(Module &M) {
+ Triple TT(M.getTargetTriple());
+ auto Arch = TT.getArch();
+
+ StringRef NewTriple;
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unrecognized architecture");
+ break;
+ case Triple::arm:
+ NewTriple = "spir-unknown-unknown";
+ break;
+ case Triple::aarch64:
+ NewTriple = "spir64-unknown-unknown";
+ break;
+ case Triple::spir:
+ case Triple::spir64:
+ DEBUG(dbgs() << "!!! Already a spir triple !!!\n");
+ }
+
+ DEBUG(dbgs() << "New triple:\t" << NewTriple << "\n");
+ M.setTargetTriple(NewTriple);
+}
+
+void addPassesForRS2SPIRV(llvm::legacy::PassManager &PassMgr) {
+ PassMgr.add(createGlobalMergePass());
+ PassMgr.add(createPromoteMemoryToRegisterPass());
+ PassMgr.add(createTransOCLMD());
+ // TODO: investigate removal of OCLTypeToSPIRV pass.
+ PassMgr.add(createOCLTypeToSPIRV());
+ PassMgr.add(createSPIRVRegularizeLLVM());
+ PassMgr.add(createSPIRVLowerConstExpr());
+ PassMgr.add(createSPIRVLowerBool());
+ PassMgr.add(createAlwaysInlinerPass());
+}
+
+bool WriteSPIRV(Module *M, llvm::raw_ostream &OS, std::string &ErrMsg) {
+ std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule());
+
+ HandleTargetTriple(*M);
+
+ bcinfo::MetadataExtractor ME(M);
+ if (!ME.extract()) {
+ errs() << "Could not extract metadata\n";
+ return false;
+ }
+ DEBUG(dbgs() << "Metadata extracted\n");
+
+ llvm::legacy::PassManager PassMgr;
+ PassMgr.add(createInlinePreparationPass(ME));
+ addPassesForRS2SPIRV(PassMgr);
+
+ std::ofstream WrapperF;
+ if (!WrapperOutputFile.empty()) {
+ WrapperF.open(WrapperOutputFile, std::ios::trunc);
+ if (!WrapperF.good()) {
+ errs() << "Could not create/open file:\t" << WrapperOutputFile << "\n";
+ return false;
+ }
+ DEBUG(dbgs() << "Wrapper output:\t" << WrapperOutputFile << "\n");
+ PassMgr.add(createReflectionPass(WrapperF, ME));
+ }
+
+ PassMgr.add(createLLVMToSPIRV(BM.get()));
+ PassMgr.run(*M);
+ DEBUG(M->dump());
+
+ if (BM->getError(ErrMsg) != SPIRVEC_Success)
+ return false;
+
+ OS << *BM;
+
+ return true;
+}
+
+bool Link(llvm::StringRef KernelFilename, llvm::StringRef WrapperFilename,
+ llvm::StringRef OutputFilename) {
+ DEBUG(dbgs() << "Linking...\n");
+
+ std::ifstream WrapperF(WrapperFilename);
+ if (!WrapperF.good()) {
+ errs() << "Cannot open file: " << WrapperFilename << "\n";
+ }
+ std::ifstream KernelF(KernelFilename);
+ if (!KernelF.good()) {
+ errs() << "Cannot open file: " << KernelFilename << "\n";
+ }
+
+ LinkerModule WrapperM(WrapperF);
+ LinkerModule KernelM(KernelF);
+
+ WrapperF.close();
+ KernelF.close();
+
+ DEBUG(dbgs() << "WrapperF:\n");
+ DEBUG(WrapperM.dump());
+ DEBUG(dbgs() << "\n~~~~~~~~~~~~~~~~~~~~~~\n\nKernelF:\n");
+ DEBUG(KernelM.dump());
+ DEBUG(dbgs() << "\n======================\n\n");
+
+ const char *const Prefix = "%rs_linker_";
+
+ for (auto *LPtr : KernelM.lines()) {
+ assert(LPtr);
+ auto &L = *LPtr;
+ size_t Pos = 0;
+ while ((Pos = L.str().find("%", Pos)) != std::string::npos) {
+ L.str().replace(Pos, 1, Prefix);
+ Pos += strlen(Prefix);
+ }
+ }
+
+ FixModuleStorageClass(KernelM);
+ DEBUG(KernelM.dump());
+
+ auto WBlocks = WrapperM.blocks();
+ auto WIt = WBlocks.begin();
+ const auto WEnd = WBlocks.end();
+
+ auto KBlocks = KernelM.blocks();
+ auto KIt = KBlocks.begin();
+ const auto KEnd = KBlocks.end();
+
+ LinkerModule OutM;
+
+ if (WIt == WEnd || KIt == KEnd)
+ return false;
+
+ const auto *HeaderB = dyn_cast<HeaderBlock>(WIt->get());
+ if (!HeaderB || !isa<HeaderBlock>(KIt->get()))
+ return false;
+
+ SmallVector<StringRef, 2> KernelNames;
+ const bool KernelsFound = HeaderB->getRSKernelNames(KernelNames);
+
+ if (!KernelsFound) {
+ errs() << "RS kernel names not found in wrapper\n";
+ return false;
+ }
+
+ // TODO: Support more than one kernel.
+ if (KernelNames.size() != 1) {
+ errs() << "Unsupported number of kernels: " << KernelNames.size() << '\n';
+ return false;
+ }
+
+ const std::string KernelName =
+ Prefix + KernelNames.front().drop_front().str();
+ DEBUG(dbgs() << "Kernel name: " << KernelName << '\n');
+
+ // Kernel's HeaderBlock is skipped - it has OpenCL-specific code that
+ // is replaced here with compute shader code.
+
+ OutM.addBlock<HeaderBlock>(*HeaderB);
+
+ if (++WIt == WEnd || ++KIt == KEnd)
+ return false;
+
+ const auto *DecorBW = dyn_cast<DecorBlock>(WIt->get());
+ if (!DecorBW || !isa<DecorBlock>(KIt->get()))
+ return false;
+
+ // Kernel's DecorBlock is skipped, because it contains OpenCL-specific code
+ // that is not needed (eg. linkage type information).
+
+ OutM.addBlock<DecorBlock>(*DecorBW);
+
+ if (++WIt == WEnd || ++KIt == KEnd)
+ return false;
+
+ const auto *TypeAndConstBW = dyn_cast<TypeAndConstBlock>(WIt->get());
+ auto *TypeAndConstBK = dyn_cast<TypeAndConstBlock>(KIt->get());
+ if (!TypeAndConstBW || !TypeAndConstBK)
+ return false;
+
+ OutM.addBlock<TypeAndConstBlock>(*TypeAndConstBW);
+ OutM.addBlock<TypeAndConstBlock>(*TypeAndConstBK);
+
+ if (++WIt == WEnd || ++KIt == KEnd)
+ return false;
+
+ const auto *VarBW = dyn_cast<VarBlock>(WIt->get());
+ auto *VarBK = dyn_cast<VarBlock>(KIt->get());
+ if (!VarBW)
+ return false;
+
+ OutM.addBlock<VarBlock>(*VarBW);
+
+ if (VarBK)
+ OutM.addBlock<VarBlock>(*VarBK);
+ else
+ --KIt;
+
+ MainFunBlock *MainB = nullptr;
+
+ while (++WIt != WEnd) {
+ auto *FunB = dyn_cast<FunctionBlock>(WIt->get());
+ if (!FunB)
+ return false;
+
+ if (auto *MB = dyn_cast<MainFunBlock>(WIt->get())) {
+ if (MainB) {
+ errs() << "More than one main function found in wrapper module\n";
+ return false;
+ }
+
+ MainB = &OutM.addBlock<MainFunBlock>(*MB);
+ } else {
+ OutM.addBlock<FunctionBlock>(*FunB);
+ }
+ }
+
+ if (!MainB) {
+ errs() << "Wrapper module has no main function\n";
+ return false;
+ }
+
+ while (++KIt != KEnd) {
+ // TODO: Check if FunDecl is a known runtime function.
+ if (isa<FunDeclBlock>(KIt->get()))
+ continue;
+
+ auto *FunB = dyn_cast<FunctionBlock>(KIt->get());
+ if (!FunB)
+ return false;
+
+ // TODO: Detect also indirect recurion.
+ if (FunB->isDirectlyRecursive()) {
+ errs() << "Function: " << FunB->getFunctionName().str()
+ << " is recursive\n";
+ return false;
+ }
+
+ OutM.addBlock<FunctionBlock>(*FunB);
+ }
+
+ OutM.fixBlockOrder();
+ if (!FixMain(OutM, *MainB, KernelName))
+ return false;
+
+ if (!FixVectorShuffles(*MainB))
+ return false;
+
+ OutM.removeUnusedFunctions();
+
+ DEBUG(dbgs() << ">>>>>>>>>>>> Output module after prelink:\n\n");
+ DEBUG(OutM.dump());
+
+ if (!FuseTypesAndConstants(OutM)) {
+ errs() << "Type fusion failed\n";
+ return false;
+ }
+
+ DEBUG(dbgs() << ">>>>>>>>>>>> Output module after value fusion:\n\n");
+ DEBUG(OutM.dump());
+
+ if (!OutM.saveToFile(OutputFilename)) {
+ errs() << "Could not save to file: " << OutputFilename << "\n";
+ return false;
+ }
+
+ return true;
+}
+
+bool FixMain(LinkerModule &LM, MainFunBlock &MainB, StringRef KernelName) {
+ MainB.replaceAllIds("%RS_SPIRV_DUMMY_", KernelName);
+
+ while (MainB.hasFunctionCalls())
+ if (!InlineFunctionCalls(LM, MainB)) {
+ errs() << "Could not inline function calls in main\n";
+ return false;
+ }
+
+ for (auto &L : MainB.lines()) {
+ if (!L.contains("OpInBoundsPtrAccessChain"))
+ continue;
+
+ if (!TranslateInBoundsPtrAccessToAccess(L))
+ return false;
+ }
+
+ return true;
+}
+
+struct FunctionCallInfo {
+ StringRef RetValName;
+ StringRef RetTy;
+ StringRef FName;
+ SmallVector<StringRef, 4> ArgNames;
+};
+
+static FunctionCallInfo GetFunctionCallInfo(const SPIRVLine &L) {
+ assert(L.contains("OpFunctionCall"));
+
+ const Optional<StringRef> Ret = L.getLHSIdentifier();
+ assert(Ret);
+
+ SmallVector<StringRef, 6> Ids;
+ L.getRHSIdentifiers(Ids);
+ assert(Ids.size() >= 2 && "No return type and function name");
+
+ const StringRef RetTy = Ids[0];
+ const StringRef FName = Ids[1];
+ SmallVector<StringRef, 4> Args(Ids.begin() + 2, Ids.end());
+
+ return {*Ret, RetTy, FName, std::move(Args)};
+}
+
+bool InlineFunctionCalls(LinkerModule &LM, MainFunBlock &MB) {
+ DEBUG(dbgs() << "InlineFunctionCalls\n");
+ MainFunBlock NewMB;
+
+ auto MLines = MB.lines();
+ auto MIt = MLines.begin();
+ const auto MEnd = MLines.end();
+ using iter_ty = decltype(MIt);
+
+ auto SkipToFunctionCall = [&MEnd, &NewMB](iter_ty &It) {
+ while (++It != MEnd && !It->contains("OpFunctionCall"))
+ NewMB.addLine(*It);
+
+ return It != MEnd;
+ };
+
+ NewMB.addLine(*MIt);
+
+ std::vector<std::pair<std::string, std::string>> NameMapping;
+
+ while (SkipToFunctionCall(MIt)) {
+ assert(MIt->contains("OpFunctionCall"));
+ const auto FInfo = GetFunctionCallInfo(*MIt);
+ DEBUG(dbgs() << "Found function call:\t" << MIt->str() << '\n');
+
+ SmallVector<Block *, 1> Callee;
+ LM.getBlocksIf(Callee, [&FInfo](Block &B) {
+ auto *FB = dyn_cast<FunctionBlock>(&B);
+ if (!FB)
+ return false;
+
+ return FB->getFunctionName() == FInfo.FName;
+ });
+
+ if (Callee.size() != 1) {
+ errs() << "Callee not found\n";
+ return false;
+ }
+
+ auto *FB = cast<FunctionBlock>(Callee.front());
+
+ if (FB->getArity() != FInfo.ArgNames.size()) {
+ errs() << "Arity mismatch (caller: " << FInfo.ArgNames.size()
+ << ", callee: " << FB->getArity() << ")\n";
+ return false;
+ }
+
+ Optional<StringRef> RetValName = FB->getRetValName();
+ if (!RetValName && !FB->isReturnTypeVoid()) {
+ errs() << "Return value not found for a function with non-void "
+ "return type.\n";
+ return false;
+ }
+
+ SmallVector<StringRef, 4> Params;
+ FB->getArgNames(Params);
+
+ if (Params.size() != FInfo.ArgNames.size()) {
+ errs() << "Params size mismatch\n";
+ return false;
+ }
+
+ for (size_t i = 0, e = FInfo.ArgNames.size(); i < e; ++i) {
+ DEBUG(dbgs() << "New param mapping: " << Params[i] << " -> "
+ << FInfo.ArgNames[i] << "\n");
+ NameMapping.emplace_back(Params[i].str(), FInfo.ArgNames[i].str());
+ }
+
+ if (RetValName) {
+ DEBUG(dbgs() << "New ret-val mapping: " << FInfo.RetValName << " -> "
+ << *RetValName << "\n");
+ NameMapping.emplace_back(FInfo.RetValName.str(), RetValName->str());
+ }
+
+ const auto Body = FB->body();
+ for (const auto &L : Body)
+ NewMB.addLine(L);
+ }
+
+ while (MIt != MEnd) {
+ NewMB.addLine(*MIt);
+ ++MIt;
+ }
+
+ std::reverse(NameMapping.begin(), NameMapping.end());
+ for (const auto &P : NameMapping) {
+ DEBUG(dbgs() << "Replace " << P.first << " with " << P.second << "\n");
+ NewMB.replaceAllIds(P.first, P.second);
+ }
+
+ MB = NewMB;
+
+ return true;
+}
+
+bool FuseTypesAndConstants(LinkerModule &LM) {
+ StringMap<std::string> TypesAndConstDefs;
+ StringMap<std::string> NameReps;
+
+ for (auto *LPtr : LM.lines()) {
+ assert(LPtr);
+ auto &L = *LPtr;
+ if (!L.contains("="))
+ continue;
+
+ SmallVector<StringRef, 4> IdsRefs;
+ L.getRHSIdentifiers(IdsRefs);
+
+ SmallVector<std::string, 4> Ids;
+ Ids.reserve(IdsRefs.size());
+ for (const auto &I : IdsRefs)
+ Ids.push_back(I.str());
+
+ for (auto &I : Ids)
+ if (NameReps.count(I) != 0) {
+ const bool Res = L.replaceId(I, NameReps[I]);
+ (void)Res;
+ assert(Res);
+ }
+
+ if (L.contains("OpType") || L.contains("OpConstant")) {
+ const auto LHS = L.getLHSIdentifier();
+ const auto RHS = L.getRHS();
+ assert(LHS);
+ assert(RHS);
+
+ if (TypesAndConstDefs.count(*RHS) != 0) {
+ NameReps.insert(
+ std::make_pair(LHS->str(), TypesAndConstDefs[RHS->str()]));
+ DEBUG(dbgs() << "New mapping: [" << LHS->str() << ", "
+ << TypesAndConstDefs[RHS->str()] << "]\n");
+ L.markAsEmpty();
+ } else {
+ TypesAndConstDefs.insert(std::make_pair(RHS->str(), LHS->str()));
+ DEBUG(dbgs() << "New val:\t" << RHS->str() << " : " << LHS->str()
+ << '\n');
+ }
+ };
+ }
+
+ LM.removeNonCode();
+
+ return true;
+}
+
+bool TranslateInBoundsPtrAccessToAccess(SPIRVLine &L) {
+ assert(L.contains(" OpInBoundsPtrAccessChain "));
+
+ SmallVector<StringRef, 4> Ids;
+ L.getRHSIdentifiers(Ids);
+
+ if (Ids.size() < 4) {
+ errs() << "OpInBoundsPtrAccessChain has not enough parameters:\n\t"
+ << L.str();
+ return false;
+ }
+
+ std::istringstream SS(L.str());
+ std::string LHS, Eq, Op;
+ SS >> LHS >> Eq >> Op;
+
+ if (LHS.empty() || Eq != "=" || Op != "OpInBoundsPtrAccessChain") {
+ errs() << "Could not decompose OpInBoundsPtrAccessChain:\n\t" << L.str();
+ return false;
+ }
+
+ constexpr size_t ElementArgPosition = 2;
+
+ std::ostringstream NewLine;
+ NewLine << LHS << " " << Eq << " OpAccessChain ";
+ for (size_t i = 0, e = Ids.size(); i != e; ++i)
+ if (i != ElementArgPosition)
+ NewLine << Ids[i].str() << " ";
+
+ L.str() = NewLine.str();
+ L.trim();
+
+ return true;
+}
+
+// Replaces UndefValues in VectorShuffles with zeros, which is always
+// safe, as the result for components marked as Undef is unused.
+// Ex. 1) OpVectorShuffle %v4uchar %a %b 0 1 2 4294967295 -->
+// OpVectorShuffle %v4uchar %a %b 0 1 2 0.
+//
+// Ex. 2) OpVectorShuffle %v4uchar %a %b 0 4294967295 3 4294967295 -->
+// OpVectorShuffle %v4uchar %a %b 0 0 3 0.
+//
+// Fix needed for the current Vulkan driver, which crashed during
+// backend compilation when case is not handled.
+bool FixVectorShuffles(MainFunBlock &MB) {
+ const StringRef UndefStr = " 4294967295 ";
+
+ for (auto &L : MB.lines()) {
+ if (!L.contains("OpVectorShuffle"))
+ continue;
+
+ L.str().push_back(' ');
+ while (L.contains(UndefStr))
+ L.replaceStr(UndefStr, " 0 ");
+
+ L.trim();
+ }
+
+ return true;
+}
+
+// This function changes all Function StorageClass use into Uniform.
+// It's needed, because llvm-spirv converter emits wrong StorageClass
+// for globals.
+// The transfromation, however, breaks legitimate uses of Function StorageClass
+// inside functions.
+//
+// Ex. 1. %ptr_Function_uint = OpTypePointer Function %uint
+// --> %ptr_Uniform_uint = OpTypePointer Uniform %uint
+//
+// Ex. 2. %gep = OpAccessChain %ptr_Function_uchar %G %uint_zero
+// --> %gep = OpAccessChain %ptr_Uniform_uchar %G %uint_zero
+//
+// TODO: Consider a better way of fixing this.
+void FixModuleStorageClass(LinkerModule &M) {
+ for (auto *LPtr : M.lines()) {
+ assert(LPtr);
+ auto &L = *LPtr;
+
+ while (L.contains(" Function"))
+ L.replaceStr(" Function", " Uniform");
+
+ while (L.contains("_Function_"))
+ L.replaceStr("_Function_", "_Uniform_");
+ }
+}
+
+} // namespace rs2spirv
diff --git a/rsov/compiler/RSSPIRVWriter.h b/rsov/compiler/RSSPIRVWriter.h
new file mode 100644
index 0000000..199d60b
--- /dev/null
+++ b/rsov/compiler/RSSPIRVWriter.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016, 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 RS_SPIRV_WRITER_H
+#define RS_SPIRV_WRITER_H
+
+#include "llvm/ADT/StringRef.h"
+
+namespace llvm {
+class Module;
+class raw_ostream;
+}
+
+namespace rs2spirv {
+
+bool WriteSPIRV(llvm::Module *M, llvm::raw_ostream &OS, std::string &ErrMsg);
+bool Link(llvm::StringRef KrrnelFilename, llvm::StringRef WrapperFilename,
+ llvm::StringRef OutputFilename);
+
+} // namespace rs2spirv
+
+#endif
diff --git a/rsov/compiler/ReflectionPass.cpp b/rsov/compiler/ReflectionPass.cpp
new file mode 100644
index 0000000..a909241
--- /dev/null
+++ b/rsov/compiler/ReflectionPass.cpp
@@ -0,0 +1,1005 @@
+/*
+ * Copyright 2016, 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 "ReflectionPass.h"
+
+#include "RSAllocationUtils.h"
+#include "bcinfo/MetadataExtractor.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/SPIRV.h"
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+
+#define DEBUG_TYPE "rs2spirv-reflection"
+
+using namespace llvm;
+
+namespace rs2spirv {
+
+namespace {
+
+// Numeric value corresponds to the number of components.
+enum class Coords : size_t { None = 0, X, XY, XYZ, Last = XYZ };
+static const StringRef CoordsNames[] = {"x", "y", "z"};
+
+struct KernelSignature {
+ std::string returnType;
+ std::string name;
+ std::string argumentType;
+ Coords coordsKind;
+
+ void dump() const {
+ dbgs() << returnType << ' ' << name << '(' << argumentType;
+ const auto CoordsNum = size_t(coordsKind);
+ for (size_t i = 0; i != CoordsNum; ++i)
+ dbgs() << ", " << CoordsNames[i];
+
+ dbgs() << ")\n";
+ }
+};
+
+std::string TypeToString(const Type *Ty) {
+ assert(Ty);
+ if (Ty->isVoidTy())
+ return "void";
+
+ if (auto *IT = dyn_cast<IntegerType>(Ty)) {
+ if (IT->getBitWidth() == 32)
+ return "int";
+ else if (IT->getBitWidth() == 8)
+ return "uchar";
+ assert(false && "Unknown integer type");
+ }
+ if (Ty->isFloatTy())
+ return "float";
+
+ if (auto *VT = dyn_cast<VectorType>(Ty)) {
+ auto *ET = VT->getElementType();
+ if (auto *IT = dyn_cast<IntegerType>(ET)) {
+ if (IT->getBitWidth() == 32)
+ return "int4";
+ else if (IT->getBitWidth() == 8)
+ return "uchar4";
+
+ assert(false && "Unknown integer vector type");
+ }
+ if (ET->isFloatTy())
+ return "float4";
+
+ llvm_unreachable("Unknown vector type");
+ }
+
+ llvm_unreachable("Unknown type");
+}
+
+enum class RSType {
+ rs_void,
+ rs_uchar,
+ rs_int,
+ rs_float,
+ rs_uchar4,
+ rs_int4,
+ rs_float4
+};
+
+RSType StrToRsTy(StringRef S) {
+ RSType Ty = StringSwitch<RSType>(S)
+ .Case("void", RSType::rs_void)
+ .Case("uchar", RSType::rs_uchar)
+ .Case("int", RSType::rs_int)
+ .Case("float", RSType::rs_float)
+ .Case("uchar4", RSType::rs_uchar4)
+ .Case("int4", RSType::rs_int4)
+ .Case("float4", RSType::rs_float4);
+ return Ty;
+}
+
+struct TypeMapping {
+ RSType RSTy;
+ bool isVectorTy;
+ // Scalar types are accessed (loaded/stored) using wider (vector) types.
+ // 'vecLen' corresponds to width of such vector type.
+ // As for vector types, 'vectorWidth' is just width of such type.
+ size_t vectorWidth;
+ std::string SPIRVTy;
+ std::string SPIRVScalarTy;
+ std::string SPIRVImageFormat;
+ // TODO: Handle different image formats for read and write.
+ std::string SPIRVImageReadType;
+
+ TypeMapping(RSType RSTy, bool IsVectorTy, size_t VectorLen,
+ StringRef SPIRVScalarTy, StringRef SPIRVImageFormat)
+ : RSTy(RSTy), isVectorTy(IsVectorTy), vectorWidth(VectorLen),
+ SPIRVScalarTy(SPIRVScalarTy), SPIRVImageFormat(SPIRVImageFormat) {
+ assert(vectorWidth != 0);
+
+ if (isVectorTy) {
+ std::ostringstream OSS;
+ OSS << "%v" << vectorWidth << SPIRVScalarTy.drop_front().str();
+ SPIRVTy = OSS.str();
+ SPIRVImageReadType = SPIRVTy;
+ return;
+ }
+
+ SPIRVTy = SPIRVScalarTy;
+ std::ostringstream OSS;
+ OSS << "%v" << vectorWidth << SPIRVScalarTy.drop_front().str();
+ SPIRVImageReadType = OSS.str();
+ }
+};
+
+class ReflectionPass : public ModulePass {
+ std::ostream &OS;
+ bcinfo::MetadataExtractor &ME;
+
+ static const std::map<RSType, TypeMapping> TypeMappings;
+
+ static const TypeMapping *getMapping(RSType RsTy) {
+ auto it = TypeMappings.find(RsTy);
+ if (it != TypeMappings.end())
+ return &it->second;
+
+ return nullptr;
+ };
+
+ static const TypeMapping *getMapping(StringRef Str) {
+ auto Ty = StrToRsTy(Str);
+ return getMapping(Ty);
+ }
+
+ static const TypeMapping *getMappingOrPrintError(StringRef Str) {
+ const auto *TM = ReflectionPass::getMapping(Str);
+ if (!TM)
+ errs() << "LLVM to SPIRV type mapping for type:\t" << Str
+ << " not found\n";
+
+ return TM;
+ }
+
+ bool emitHeader(const Module &M);
+ bool emitDecorations(const Module &M,
+ const SmallVectorImpl<RSAllocationInfo> &RSAllocs);
+ void emitCommonTypes();
+ bool extractKernelSignatures(const Module &M,
+ SmallVectorImpl<KernelSignature> &Out);
+ bool emitKernelTypes(const KernelSignature &Kernel);
+ bool emitInputImage(const KernelSignature &Kernel);
+ void emitGLGlobalInput();
+ bool emitOutputImage(const KernelSignature &Kernel);
+ bool emitRSAllocImages(const SmallVectorImpl<RSAllocationInfo> &RSAllocs);
+ bool emitConstants(const KernelSignature &Kernel);
+ void emitRTFunctions();
+ bool emitRSAllocFunctions(
+ Module &M, const SmallVectorImpl<RSAllocationInfo> &RSAllocs,
+ const SmallVectorImpl<RSAllocationCallInfo> &RSAllocAccesses);
+ bool emitMain(const KernelSignature &Kernel,
+ const SmallVectorImpl<RSAllocationInfo> &RSAllocs);
+
+public:
+ static char ID;
+ explicit ReflectionPass(std::ostream &OS, bcinfo::MetadataExtractor &ME)
+ : ModulePass(ID), OS(OS), ME(ME) {}
+
+ const char *getPassName() const override { return "ReflectionPass"; }
+
+ bool runOnModule(Module &M) override {
+ DEBUG(dbgs() << "ReflectionPass\n");
+
+ if (!emitHeader(M)) {
+ errs() << "Emiting header failed\n";
+ return false;
+ }
+
+ SmallVector<RSAllocationInfo, 2> RSAllocs;
+ if (!getRSAllocationInfo(M, RSAllocs)) {
+ errs() << "Extracting rs_allocation info failed\n";
+ return false;
+ }
+
+ SmallVector<RSAllocationCallInfo, 4> RSAllocAccesses;
+ if (!getRSAllocAccesses(RSAllocs, RSAllocAccesses)) {
+ errs() << "Extracting rsGEA/rsSEA info failed\n";
+ return false;
+ }
+
+ if (!emitDecorations(M, RSAllocs)) {
+ errs() << "Emiting decorations failed\n";
+ return false;
+ }
+
+ emitCommonTypes();
+
+ SmallVector<KernelSignature, 4> Kernels;
+ if (!extractKernelSignatures(M, Kernels)) {
+ errs() << "Extraction of kernels failed\n";
+ return false;
+ }
+
+ if (Kernels.size() != 1) {
+ errs() << "Non single-kernel modules are not supported\n";
+ return false;
+ }
+ const auto &Kernel = Kernels.front();
+
+ if (!emitKernelTypes(Kernel)) {
+ errs() << "Emitting kernel types failed\n";
+ return false;
+ }
+
+ if (!emitInputImage(Kernel)) {
+ errs() << "Emitting input image failed\n";
+ return false;
+ }
+
+ emitGLGlobalInput();
+
+ if (!emitOutputImage(Kernel)) {
+ errs() << "Emitting output image failed\n";
+ return false;
+ }
+
+ if (!emitRSAllocImages(RSAllocs)) {
+ errs() << "Emitting rs_allocation images failed\n";
+ return false;
+ }
+
+ if (!emitConstants(Kernel)) {
+ errs() << "Emitting constants failed\n";
+ return false;
+ }
+
+ emitRTFunctions();
+
+ if (!emitRSAllocFunctions(M, RSAllocs, RSAllocAccesses)) {
+ errs() << "Emitting rs_allocation runtime functions failed\n";
+ return false;
+ }
+
+ if (!emitMain(Kernel, RSAllocs)) {
+ errs() << "Emitting main failed\n";
+ return false;
+ }
+
+ // Return false, as the module is not modified.
+ return false;
+ }
+};
+
+// TODO: Add other types: bool, double, char, uchar, long, ulong
+// and their vector counterparts.
+// TODO: Support vector types of width different than 4. eg. float3.
+const std::map<RSType, TypeMapping> ReflectionPass::TypeMappings = {
+ {RSType::rs_void, {RSType::rs_void, false, 1, "%void", ""}},
+ {RSType::rs_uchar, {RSType::rs_uchar, false, 4, "%uchar", "R8ui"}},
+ {RSType::rs_int, {RSType::rs_void, false, 4, "%int", "R32i"}},
+ {RSType::rs_float, {RSType::rs_float, false, 4, "%float", "R32f"}},
+ {RSType::rs_uchar4, {RSType::rs_uchar4, true, 4, "%uchar", "Rgba8ui"}},
+ {RSType::rs_int4, {RSType::rs_int4, true, 4, "%int", "Rgba32i"}},
+ {RSType::rs_float4, {RSType::rs_float4, true, 4, "%float", "Rgba32f"}}};
+};
+
+char ReflectionPass::ID = 0;
+
+ModulePass *createReflectionPass(std::ostream &OS,
+ bcinfo::MetadataExtractor &ME) {
+ return new ReflectionPass(OS, ME);
+}
+
+bool ReflectionPass::emitHeader(const Module &M) {
+ DEBUG(dbgs() << "emitHeader\n");
+
+ OS << "; SPIR-V\n"
+ "; Version: 1.0\n"
+ "; Generator: rs2spirv;\n"
+ "; Bound: 1024\n"
+ "; Schema: 0\n"
+ " OpCapability Shader\n"
+ " OpCapability StorageImageWriteWithoutFormat\n"
+ " OpCapability Addresses\n"
+ " %glsl_ext_ins = OpExtInstImport \"GLSL.std.450\"\n"
+ " OpMemoryModel Physical32 GLSL450\n"
+ " OpEntryPoint GLCompute %main \"main\" %global_invocation_id\n"
+ " OpExecutionMode %main LocalSize 1 1 1\n"
+ " OpSource GLSL 450\n"
+ " OpSourceExtension \"GL_ARB_separate_shader_objects\"\n"
+ " OpSourceExtension \"GL_ARB_shading_language_420pack\"\n"
+ " OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"\n"
+ " OpSourceExtension \"GL_GOOGLE_include_directive\"\n";
+
+ const size_t RSKernelNum = ME.getExportForEachSignatureCount();
+
+ if (RSKernelNum == 0)
+ return false;
+
+ const char **RSKernelNames = ME.getExportForEachNameList();
+
+ OS << " %RS_KERNELS = OpString \"";
+
+ for (size_t i = 0; i < RSKernelNum; ++i)
+ if (RSKernelNames[i] != StringRef("root"))
+ OS << '%' << RSKernelNames[i] << " ";
+
+ OS << "\"\n";
+
+ return true;
+}
+
+bool ReflectionPass::emitDecorations(
+ const Module &M, const SmallVectorImpl<RSAllocationInfo> &RSAllocs) {
+ DEBUG(dbgs() << "emitDecorations\n");
+
+ OS << "\n"
+ " OpDecorate %global_invocation_id BuiltIn GlobalInvocationId\n"
+ " OpDecorate %input_image DescriptorSet 0\n"
+ " OpDecorate %input_image Binding 0\n"
+ " OpDecorate %input_image NonWritable\n"
+ " OpDecorate %output_image DescriptorSet 0\n"
+ " OpDecorate %output_image Binding 1\n"
+ " OpDecorate %output_image NonReadable\n";
+
+ const auto GlobalsB = M.globals().begin();
+ const auto GlobalsE = M.globals().end();
+ const auto Found =
+ std::find_if(GlobalsB, GlobalsE, [](const GlobalVariable &GV) {
+ return GV.getName() == "__GPUBlock";
+ });
+
+ if (Found == GlobalsE)
+ return true; // GPUBlock not found - not an error by itself.
+
+ const GlobalVariable &G = *Found;
+
+ DEBUG(dbgs() << "Found GPUBlock:\t");
+ DEBUG(G.dump());
+
+ bool IsCorrectTy = false;
+ if (const auto *PtrTy = dyn_cast<PointerType>(G.getType())) {
+ if (auto *StructTy = dyn_cast<StructType>(PtrTy->getElementType())) {
+ IsCorrectTy = true;
+
+ const auto &DLayout = M.getDataLayout();
+ const auto *SLayout = DLayout.getStructLayout(StructTy);
+ assert(SLayout);
+
+ for (size_t i = 0, e = StructTy->getNumElements(); i != e; ++i)
+ OS << " OpMemberDecorate %rs_linker_struct___GPUBuffer " << i
+ << " Offset " << SLayout->getElementOffset(i) << '\n';
+ }
+ }
+
+ if (!IsCorrectTy) {
+ errs() << "GPUBlock is not of expected type:\t";
+ G.print(errs());
+ G.getType()->print(errs());
+ return false;
+ }
+
+ OS << " OpDecorate %rs_linker_struct___GPUBuffer BufferBlock\n";
+ OS << " OpDecorate %rs_linker___GPUBlock DescriptorSet 0\n";
+ OS << " OpDecorate %rs_linker___GPUBlock Binding 2\n";
+
+ size_t BindingNum = 3;
+
+ for (const auto &A : RSAllocs) {
+ OS << " OpDecorate " << A.VarName << "_var DescriptorSet 0\n";
+ OS << " OpDecorate " << A.VarName << "_var Binding " << BindingNum
+ << '\n';
+ ++BindingNum;
+ }
+
+ return true;
+}
+
+void ReflectionPass::emitCommonTypes() {
+ DEBUG(dbgs() << "emitCommonTypes\n");
+
+ OS << "\n\n"
+ "%void = OpTypeVoid\n"
+ "%fun_void = OpTypeFunction %void\n"
+ "%float = OpTypeFloat 32\n"
+ "%v2float = OpTypeVector %float 2\n"
+ "%v3float = OpTypeVector %float 3\n"
+ "%v4float = OpTypeVector %float 4\n"
+ "%int = OpTypeInt 32 1\n"
+ "%v2int = OpTypeVector %int 2\n"
+ "%v4int = OpTypeVector %int 4\n"
+ "%uchar = OpTypeInt 8 0\n"
+ "%v2uchar = OpTypeVector %uchar 2\n"
+ "%v3uchar = OpTypeVector %uchar 3\n"
+ "%v4uchar = OpTypeVector %uchar 4\n"
+ "%uint = OpTypeInt 32 0\n"
+ "%v2uint = OpTypeVector %uint 2\n"
+ "%v3uint = OpTypeVector %uint 3\n"
+ "%v4uint = OpTypeVector %uint 4\n"
+ "%fun_f3_uc3 = OpTypeFunction %v3float %v3uchar\n"
+ "%fun_f3_u3 = OpTypeFunction %v3float %v3uint\n"
+ "%fun_f4_uc4 = OpTypeFunction %v4float %v4uchar\n"
+ "%fun_uc3_f3 = OpTypeFunction %v3uchar %v3float\n"
+ "%fun_u3_f3 = OpTypeFunction %v3uint %v3float\n"
+ "%fun_uc4_f4 = OpTypeFunction %v4uchar %v4float\n"
+ "%fun_uc4_u4 = OpTypeFunction %v4uchar %v4uint\n"
+ "%fun_u4_uc4 = OpTypeFunction %v4uint %v4uchar\n"
+ "%fun_f_f = OpTypeFunction %float %float\n"
+ "%fun_f_ff = OpTypeFunction %float %float %float\n"
+ "%fun_f_fff = OpTypeFunction %float %float %float %float\n"
+ "%fun_f_f2f2 = OpTypeFunction %float %v2float %v2float\n"
+ "%fun_f_f3f3 = OpTypeFunction %float %v3float %v3float\n"
+ "%fun_f3_f3ff = OpTypeFunction %v3float %v3float %float %float\n"
+ "%fun_i_iii = OpTypeFunction %int %int %int %int\n"
+ "%fun_uc_uu = OpTypeFunction %uchar %uint %uint\n"
+ "%fun_u_uu = OpTypeFunction %uint %uint %uint\n"
+ "%fun_u_uuu = OpTypeFunction %uint %uint %uint %uint\n"
+ "%fun_u3_u3uu = OpTypeFunction %v3uint %v3uint %uint %uint\n";
+}
+
+static Coords GetCoordsKind(const Function &F) {
+ if (F.arg_size() <= 1)
+ return Coords::None;
+
+ DEBUG(F.getFunctionType()->dump());
+
+ SmallVector<const Argument *, 4> Args;
+ Args.reserve(F.arg_size());
+ for (const auto &Arg : F.args())
+ Args.push_back(&Arg);
+
+ auto IsInt32 = [](const Argument *Arg) {
+ assert(Arg);
+ auto *Ty = Arg->getType();
+ auto IntTy = dyn_cast<IntegerType>(Ty);
+ if (!IntTy)
+ return false;
+
+ return IntTy->getBitWidth() == 32;
+ };
+
+ size_t LastInt32Num = 0;
+ size_t XPos = -1; // npos - not found.
+ auto RIt = Args.rbegin();
+ const auto REnd = Args.rend();
+ while (RIt != REnd && IsInt32(*RIt)) {
+ if ((*RIt)->getName() == "x")
+ XPos = Args.size() - 1 - LastInt32Num;
+
+ ++LastInt32Num;
+ ++RIt;
+ }
+
+ DEBUG(dbgs() << "Original number of last i32's: " << LastInt32Num << '\n');
+ DEBUG(dbgs() << "X found at position: " << XPos << '\n');
+ if (XPos == size_t(-1) || Args.size() - XPos > size_t(Coords::Last))
+ return Coords::None;
+
+ // Check remaining coordinate names.
+ for (size_t i = 1, c = XPos + 1, e = Args.size(); c != e; ++i, ++c)
+ if (Args[c]->getName() != CoordsNames[i])
+ return Coords::None;
+
+ DEBUG(dbgs() << "Coords: not none!\n");
+
+ return Coords(Args.size() - XPos);
+}
+
+bool ReflectionPass::extractKernelSignatures(
+ const Module &M, SmallVectorImpl<KernelSignature> &Out) {
+ DEBUG(dbgs() << "extractKernelSignatures\n");
+
+ for (const auto &F : M.functions()) {
+ if (F.isDeclaration())
+ continue;
+
+ const auto CoordsKind = GetCoordsKind(F);
+ const auto CoordsNum = unsigned(CoordsKind);
+ if (F.arg_size() != CoordsNum + 1) {
+ // TODO: Handle different arrities (and lack of return value).
+ errs() << "Unsupported kernel signature.\n";
+ return false;
+ }
+
+ const auto *FT = F.getFunctionType();
+ const auto *RT = FT->getReturnType();
+ const auto *ArgT = FT->params()[0];
+ Out.push_back(
+ {TypeToString(RT), F.getName(), TypeToString(ArgT), GetCoordsKind(F)});
+ DEBUG(Out.back().dump());
+ }
+
+ if (Out.size() != 1) {
+ // TODO: recognize non-kernel functions and don't bail out here.
+ errs() << "Unsupported number of kernels\n";
+ return false;
+ }
+
+ return true;
+}
+
+bool ReflectionPass::emitKernelTypes(const KernelSignature &Kernel) {
+ DEBUG(dbgs() << "emitKernelTypes\n");
+
+ const auto *RTMapping = getMappingOrPrintError(Kernel.returnType);
+ const auto *ArgTMapping = getMappingOrPrintError(Kernel.argumentType);
+
+ if (!RTMapping || !ArgTMapping)
+ return false;
+
+ OS << '\n' << "%kernel_function_ty = OpTypeFunction " << RTMapping->SPIRVTy
+ << ' ' << ArgTMapping->SPIRVTy;
+
+ const auto CoordsNum = unsigned(Kernel.coordsKind);
+ for (size_t i = 0; i != CoordsNum; ++i)
+ OS << " %uint";
+
+ OS << '\n';
+
+ OS << "%ptr_function_ty = OpTypePointer Function " << RTMapping->SPIRVTy
+ << "\n";
+ OS << "%ptr_function_access_ty = OpTypePointer Function "
+ << RTMapping->SPIRVImageReadType << "\n\n";
+
+ return true;
+}
+
+bool ReflectionPass::emitInputImage(const KernelSignature &Kernel) {
+ DEBUG(dbgs() << "emitInputImage\n");
+
+ const auto *ArgTMapping = getMappingOrPrintError(Kernel.argumentType);
+ if (!ArgTMapping)
+ return false;
+
+ OS << "%input_image_ty = OpTypeImage " << ArgTMapping->SPIRVScalarTy
+ << " 2D 0 0 0 2 " << ArgTMapping->SPIRVImageFormat << '\n';
+
+ OS << "%input_image_ptr_ty = OpTypePointer UniformConstant "
+ << "%input_image_ty\n";
+
+ OS << "%input_image = OpVariable %input_image_ptr_ty UniformConstant\n";
+
+ return true;
+}
+
+void ReflectionPass::emitGLGlobalInput() {
+ DEBUG(dbgs() << "emitGLGlobalInput\n");
+
+ OS << '\n' << "%global_input_ptr_ty = OpTypePointer Input %v3uint\n"
+ << "%global_invocation_id = OpVariable %global_input_ptr_ty Input\n";
+}
+
+bool ReflectionPass::emitOutputImage(const KernelSignature &Kernel) {
+ DEBUG(dbgs() << "emitOutputImage\n");
+
+ const auto *RTMapping = getMappingOrPrintError(Kernel.returnType);
+ if (!RTMapping)
+ return false;
+
+ OS << '\n';
+ OS << "%output_image_ty = OpTypeImage " << RTMapping->SPIRVScalarTy
+ << " 2D 0 0 0 2 " << RTMapping->SPIRVImageFormat << '\n'
+ << "%output_image_ptr_ty = OpTypePointer UniformConstant "
+ << "%output_image_ty\n";
+
+ OS << "%output_image = OpVariable %output_image_ptr_ty Image\n";
+
+ return true;
+}
+
+bool ReflectionPass::emitRSAllocImages(
+ const SmallVectorImpl<RSAllocationInfo> &RSAllocs) {
+ DEBUG(dbgs() << "emitRSAllocImages\n");
+
+ for (const auto &A : RSAllocs) {
+ if (!A.RSElementType) {
+ errs() << "Type of variable " << A.VarName << " not infered.\n";
+ return false;
+ }
+
+ const auto *AMapping = getMappingOrPrintError(*A.RSElementType);
+ if (!AMapping)
+ return false;
+
+ OS << '\n' << A.VarName << "_image_ty"
+ << " = OpTypeImage " << AMapping->SPIRVScalarTy << " 2D 0 0 0 2 "
+ << AMapping->SPIRVImageFormat << '\n' << A.VarName << "_image_ptr_ty"
+ << " = OpTypePointer UniformConstant " << A.VarName << "_image_ty\n";
+
+ OS << A.VarName << "_var = OpVariable " << A.VarName
+ << "_image_ptr_ty Image\n";
+ }
+
+ return true;
+}
+
+bool ReflectionPass::emitConstants(const KernelSignature &Kernel) {
+ DEBUG(dbgs() << "emitConstants\n");
+
+ OS << "\n"
+ "%uint_zero = OpConstant %uint 0\n"
+ "%float_zero = OpConstant %float 0\n";
+
+ return true;
+}
+
+static std::string GenerateConversionFun(const char *Name, const char *FType,
+ const char *From, const char *To,
+ const char *ConversionOp) {
+ std::ostringstream OS;
+
+ OS << "\n"
+ << "%rs_linker_" << Name << " = OpFunction " << To << " Pure " << FType
+ << "\n"
+ << "%param" << Name << " = OpFunctionParameter " << From << "\n"
+ << "%label" << Name << " = OpLabel\n"
+ << "%res" << Name << " = " << ConversionOp << " " << To << " %param"
+ << Name << "\n"
+ << " OpReturnValue %res" << Name << "\n"
+ << " OpFunctionEnd\n";
+
+ return OS.str();
+}
+
+static std::string GenerateEISFun(const char *Name, const char *FType,
+ const char *RType,
+ const SmallVector<const char *, 4> &ArgTypes,
+ const char *InstName) {
+ std::ostringstream OS;
+
+ OS << '\n' << "%rs_linker_" << Name << " = OpFunction " << RType << " Pure "
+ << FType << '\n';
+
+ for (size_t i = 0, e = ArgTypes.size(); i < e; ++i)
+ OS << "%param" << Name << i << " = OpFunctionParameter " << ArgTypes[i]
+ << "\n";
+
+ OS << "%label" << Name << " = OpLabel\n"
+ << "%res" << Name << " = "
+ << "OpExtInst " << RType << " %glsl_ext_ins " << InstName;
+
+ for (size_t i = 0, e = ArgTypes.size(); i < e; ++i)
+ OS << " %param" << Name << i;
+
+ OS << '\n' << " OpReturnValue %res" << Name << "\n"
+ << " OpFunctionEnd\n";
+
+ return OS.str();
+}
+
+// This SPIRV function generator relies heavily on future inlining.
+// Currently, the inliner doesn't perform any type checking - it blindly
+// maps function parameters to supplied parameters at call site.
+// It's non-trivial to generate correct SPIRV function signature based only
+// on the LLVM one, and the current design doesn't allow lazy type generation.
+//
+// TODO: Consider less horrible generator design that doesn't rely on lack of
+// type checking in the inliner.
+static std::string GenerateRSGEA(const char *Name, const char *RType,
+ StringRef LoadName, Coords CoordsKind) {
+ assert(CoordsKind != Coords::None);
+ std::ostringstream OS;
+
+ OS << "\n"
+ << "%rs_linker_" << Name << " = OpFunction " << RType
+ << " None %rs_inliner_placeholder_ty\n";
+
+ // Since the inliner doesn't perform type checking, function and parameter
+ // types can be anything. %rs_inliner_placeholder_ty is just a placeholder
+ // name that will disappear after inlining.
+
+ OS << "%rs_drop_param_" << Name << " = OpFunctionParameter "
+ << "%rs_inliner_placeholder_ty\n";
+
+ for (size_t i = 0, e = size_t(CoordsKind); i != e; ++i)
+ OS << "%param" << Name << '_' << CoordsNames[i].str()
+ << " = OpFunctionParameter %uint\n";
+
+ OS << "%label" << Name << " = OpLabel\n";
+ OS << "%arg" << Name << " = OpCompositeConstruct %v" << size_t(CoordsKind)
+ << "uint ";
+
+ for (size_t i = 0, e = size_t(CoordsKind); i != e; ++i)
+ OS << "%param" << Name << '_' << CoordsNames[i].str() << ' ';
+
+ OS << '\n';
+
+ OS << "%read" << Name << " = OpImageRead " << RType << ' ' << LoadName.str()
+ << " %arg" << Name << '\n';
+ OS << " OpReturnValue %read" << Name << '\n';
+ OS << " OpFunctionEnd\n";
+
+ return OS.str();
+}
+
+// The same remarks as to GenerateRSGEA apply to SEA function generator.
+static std::string GenerateRSSEA(const char *Name, StringRef LoadName,
+ Coords CoordsKind) {
+ assert(CoordsKind != Coords::None);
+ std::ostringstream OS;
+
+ // %rs_inliner_placeholder_ty will disappear after inlining.
+ OS << "\n"
+ << "%rs_linker_" << Name << " = OpFunction %void None "
+ << "%rs_inliner_placeholder_ty\n";
+
+ OS << "%rs_placeholder_param_" << Name << " = OpFunctionParameter "
+ << "%rs_inliner_placeholder_ty\n";
+ OS << "%param" << Name << "_new_val = OpFunctionParameter "
+ << "%rs_inliner_placeholder_ty\n";
+
+ for (size_t i = 0, e = size_t(CoordsKind); i != e; ++i)
+ OS << "%param" << Name << '_' << CoordsNames[i].str()
+ << " = OpFunctionParameter %uint\n";
+
+ OS << "%label" << Name << " = OpLabel\n";
+ OS << "%arg" << Name << " = OpCompositeConstruct %v" << size_t(CoordsKind)
+ << "uint ";
+
+ for (size_t i = 0, e = size_t(CoordsKind); i != e; ++i)
+ OS << "%param" << Name << '_' << CoordsNames[i].str() << ' ';
+
+ OS << '\n';
+
+ OS << "OpImageWrite " << LoadName.str() << " %arg" << Name << " %param"
+ << Name << "_new_val\n";
+ OS << " OpReturn\n";
+ OS << " OpFunctionEnd\n";
+
+ return OS.str();
+}
+
+void ReflectionPass::emitRTFunctions() {
+ DEBUG(dbgs() << "emitRTFunctions\n");
+
+ // TODO: Emit other runtime functions.
+ // TODO: Generate libary file instead of generating functions below
+ // every compilation.
+
+ // Use uints as Khronos' SPIRV converter turns LLVM's i32s into uints.
+
+ OS << GenerateConversionFun("_Z14convert_float4Dv4_h", "%fun_f4_uc4",
+ "%v4uchar", "%v4float", "OpConvertUToF");
+
+ OS << GenerateConversionFun("_Z14convert_uchar4Dv4_f", "%fun_uc4_f4",
+ "%v4float", "%v4uchar", "OpConvertFToU");
+
+ OS << GenerateConversionFun("_Z14convert_float3Dv3_h", "%fun_f3_uc3",
+ "%v3uchar", "%v3float", "OpConvertUToF");
+
+ OS << GenerateConversionFun("_Z14convert_uchar3Dv3_f", "%fun_uc3_f3",
+ "%v3float", "%v3uchar", "OpConvertFToU");
+
+ OS << GenerateConversionFun("_Z12convert_int3Dv3_f", "%fun_u3_f3", "%v3float",
+ "%v3uint", "OpConvertFToU");
+
+ OS << GenerateConversionFun("_Z14convert_uchar3Dv3_i", "%fun_uc3_u3",
+ "%v3uint", "%v3uchar", "OpUConvert");
+
+ OS << GenerateConversionFun("_Z14convert_uchar4Dv4_j", "%fun_uc4_u4",
+ "%v4uint", "%v4uchar", "OpUConvert");
+
+ OS << GenerateConversionFun("_Z13convert_uint4Dv4_h", "%fun_u4_uc4",
+ "%v4uchar", "%v4uint", "OpUConvert");
+
+ OS << GenerateEISFun("_Z3sinf", "%fun_f_f", "%float", {"%float"}, "Sin");
+ OS << GenerateEISFun("_Z4sqrtf", "%fun_f_f", "%float", {"%float"}, "Sqrt");
+ OS << GenerateEISFun("_Z10native_expf", "%fun_f_f", "%float", {"%float"},
+ "Exp");
+ OS << GenerateEISFun("_Z3maxii", "%fun_u_uu", "%uint", {"%uint", "%uint"},
+ "SMax");
+ OS << GenerateEISFun("_Z3minii", "%fun_u_uu", "%uint", {"%uint", "%uint"},
+ "SMin");
+ OS << GenerateEISFun("_Z3maxff", "%fun_f_ff", "%float", {"%float", "%float"},
+ "FMax");
+ OS << GenerateEISFun("_Z3minff", "%fun_f_ff", "%float", {"%float", "%float"},
+ "FMin");
+ OS << GenerateEISFun("_Z5clampfff", "%fun_f_fff", "%float",
+ {"%float", "%float", "%float"}, "FClamp");
+ OS << GenerateEISFun("_Z5clampiii", "%fun_u_uuu", "%uint",
+ {"%uint", "%uint", "%uint"}, "SClamp");
+
+ OS << R"(
+%rs_linker__Z3dotDv2_fS_ = OpFunction %float Pure %fun_f_f2f2
+%param_Z3dotDv2_fS_0 = OpFunctionParameter %v2float
+%param_Z3dotDv2_fS_1 = OpFunctionParameter %v2float
+%label_Z3dotDv2_fS = OpLabel
+%res_Z3dotDv2_fS = OpDot %float %param_Z3dotDv2_fS_0 %param_Z3dotDv2_fS_1
+ OpReturnValue %res_Z3dotDv2_fS
+ OpFunctionEnd
+)";
+
+ OS << R"(
+%rs_linker__Z3dotDv3_fS_ = OpFunction %float Pure %fun_f_f3f3
+%param_Z3dotDv3_fS_0 = OpFunctionParameter %v3float
+%param_Z3dotDv3_fS_1 = OpFunctionParameter %v3float
+%label_Z3dotDv3_fS = OpLabel
+%res_Z3dotDv3_fS = OpDot %float %param_Z3dotDv3_fS_0 %param_Z3dotDv3_fS_1
+ OpReturnValue %res_Z3dotDv3_fS
+ OpFunctionEnd
+)";
+
+ OS << R"(
+%rs_linker_rsUnpackColor8888 = OpFunction %v4float Pure %fun_f4_uc4
+%paramrsUnpackColor88880 = OpFunctionParameter %v4uchar
+%labelrsUnpackColor8888 = OpLabel
+%castedUnpackColor8888 = OpBitcast %uint %paramrsUnpackColor88880
+%resrsUnpackColor8888 = OpExtInst %v4float %glsl_ext_ins UnpackUnorm4x8 %castedUnpackColor8888
+ OpReturnValue %resrsUnpackColor8888
+ OpFunctionEnd
+)";
+
+ OS << R"(
+%rs_linker__Z17rsPackColorTo8888Dv4_f = OpFunction %v4uchar Pure %fun_uc4_f4
+%param_Z17rsPackColorTo8888Dv4_f0 = OpFunctionParameter %v4float
+%label_Z17rsPackColorTo8888Dv4_f = OpLabel
+%res_Z17rsPackColorTo8888Dv4_f = OpExtInst %uint %glsl_ext_ins PackUnorm4x8 %param_Z17rsPackColorTo8888Dv4_f0
+%casted_Z17rsPackColorTo8888Dv4_f = OpBitcast %v4uchar %res_Z17rsPackColorTo8888Dv4_f
+ OpReturnValue %casted_Z17rsPackColorTo8888Dv4_f
+ OpFunctionEnd
+)";
+
+ OS << R"(
+%rs_linker__Z5clampDv3_fff = OpFunction %v3float Pure %fun_f3_f3ff
+%param_Z5clampDv3_fff0 = OpFunctionParameter %v3float
+%param_Z5clampDv3_fff1 = OpFunctionParameter %float
+%param_Z5clampDv3_fff2 = OpFunctionParameter %float
+%label_Z5clampDv3_fff = OpLabel
+%arg1_Z5clampDv3_fff = OpCompositeConstruct %v3float %param_Z5clampDv3_fff1 %param_Z5clampDv3_fff1 %param_Z5clampDv3_fff1
+%arg2_Z5clampDv3_fff = OpCompositeConstruct %v3float %param_Z5clampDv3_fff2 %param_Z5clampDv3_fff2 %param_Z5clampDv3_fff2
+%res_Z5clampDv3_fff = OpExtInst %v3float %glsl_ext_ins FClamp %param_Z5clampDv3_fff0 %arg1_Z5clampDv3_fff %arg2_Z5clampDv3_fff
+ OpReturnValue %res_Z5clampDv3_fff
+ OpFunctionEnd
+)";
+
+ OS << R"(
+%rs_linker__Z5clampDv3_iii = OpFunction %v3uint Pure %fun_u3_u3uu
+%param_Z5clampDv3_iii0 = OpFunctionParameter %v3uint
+%param_Z5clampDv3_iii1 = OpFunctionParameter %uint
+%param_Z5clampDv3_iii2 = OpFunctionParameter %uint
+%label_Z5clampDv3_iii = OpLabel
+%arg1_Z5clampDv3_iii = OpCompositeConstruct %v3uint %param_Z5clampDv3_iii1 %param_Z5clampDv3_iii1 %param_Z5clampDv3_iii1
+%arg2_Z5clampDv3_iii = OpCompositeConstruct %v3uint %param_Z5clampDv3_iii2 %param_Z5clampDv3_iii2 %param_Z5clampDv3_iii2
+%res_Z5clampDv3_iii = OpExtInst %v3uint %glsl_ext_ins UClamp %param_Z5clampDv3_iii0 %arg1_Z5clampDv3_iii %arg2_Z5clampDv3_iii
+ OpReturnValue %res_Z5clampDv3_iii
+ OpFunctionEnd
+)";
+}
+
+bool ReflectionPass::emitRSAllocFunctions(
+ Module &M, const SmallVectorImpl<RSAllocationInfo> &RSAllocs,
+ const SmallVectorImpl<RSAllocationCallInfo> &RSAllocAccesses) {
+ DEBUG(dbgs() << "emitRSAllocFunctions\n");
+
+ for (const auto &Access : RSAllocAccesses) {
+ solidifyRSAllocAccess(M, Access);
+
+ auto *Fun = Access.FCall->getCalledFunction();
+ if (!Fun)
+ return false;
+
+ const auto FName = Fun->getName();
+ auto *ETMapping = getMappingOrPrintError(Access.RSElementTy);
+ if (!ETMapping)
+ return false;
+
+ const auto ElementTy = ETMapping->SPIRVTy;
+ const std::string LoadName = Access.RSAlloc.VarName + "_load";
+
+ if (Access.Kind == RSAllocAccessKind::GEA)
+ OS << GenerateRSGEA(FName.str().c_str(), ElementTy.c_str(),
+ LoadName.c_str(), Coords::XY);
+ else
+ OS << GenerateRSSEA(FName.str().c_str(), LoadName.c_str(), Coords::XY);
+ }
+
+ return true;
+}
+
+bool ReflectionPass::emitMain(
+ const KernelSignature &Kernel,
+ const SmallVectorImpl<RSAllocationInfo> &RSAllocs) {
+ DEBUG(dbgs() << "emitMain\n");
+
+ const auto *RTMapping = getMappingOrPrintError(Kernel.returnType);
+ const auto *ArgTMapping = getMappingOrPrintError(Kernel.argumentType);
+
+ if (!RTMapping || !ArgTMapping)
+ return false;
+
+ OS << '\n';
+ OS << " %main = OpFunction %void None %fun_void\n"
+ "%lablel_main = OpLabel\n"
+ "%input_pixel = OpVariable %ptr_function_access_ty Function\n"
+ " %res = OpVariable %ptr_function_ty Function\n"
+ " %image_load = OpLoad %input_image_ty %input_image\n"
+ "%coords_load = OpLoad %v3uint %global_invocation_id\n"
+ " %coords_x = OpCompositeExtract %uint %coords_load 0\n"
+ " %coords_y = OpCompositeExtract %uint %coords_load 1\n"
+ " %coords_z = OpCompositeExtract %uint %coords_load 2\n"
+ " %shuffled = OpVectorShuffle %v2uint %coords_load %coords_load 0 1\n"
+ " %bitcasted = OpBitcast %v2int %shuffled\n";
+
+ OS << " %image_read = OpImageRead " << ArgTMapping->SPIRVImageReadType
+ << " %image_load %bitcasted\n"
+ " OpStore %input_pixel %image_read\n";
+
+ // TODO: Handle vector types of width different than 4.
+ if (RTMapping->isVectorTy) {
+ OS << " %input_load = OpLoad " << ArgTMapping->SPIRVTy << " %input_pixel\n";
+ } else {
+ OS << "%input_access_chain = OpAccessChain %ptr_function_ty "
+ "%input_pixel %uint_zero\n"
+ << " %input_load = OpLoad " << ArgTMapping->SPIRVTy
+ << " %input_access_chain\n";
+ }
+
+ for (const auto &A : RSAllocs)
+ OS << A.VarName << "_load = OpLoad " << A.VarName << "_image_ty "
+ << A.VarName << "_var\n";
+
+ OS << "%kernel_call = OpFunctionCall " << ArgTMapping->SPIRVTy
+ << " %RS_SPIRV_DUMMY_ %input_load";
+
+ const auto CoordsNum = size_t(Kernel.coordsKind);
+ for (size_t i = 0; i != CoordsNum; ++i)
+ OS << " %coords_" << CoordsNames[i].str();
+
+ OS << '\n';
+
+ OS << " OpStore %res %kernel_call\n"
+ "%output_load = OpLoad %output_image_ty %output_image\n";
+ OS << " %res_load = OpLoad " << RTMapping->SPIRVTy << " %res\n";
+
+ if (!RTMapping->isVectorTy) {
+ OS << "%composite_constructed = OpCompositeConstruct "
+ << RTMapping->SPIRVImageReadType;
+ for (size_t i = 0; i < RTMapping->vectorWidth; ++i)
+ OS << " %res_load";
+
+ OS << "\n"
+ " OpImageWrite %output_load %bitcasted "
+ "%composite_constructed\n";
+
+ } else {
+ OS << " OpImageWrite %output_load %bitcasted %res_load\n";
+ }
+
+ OS << " OpReturn\n"
+ " OpFunctionEnd\n";
+
+ OS << "%RS_SPIRV_DUMMY_ = OpFunction " << RTMapping->SPIRVTy
+ << " None %kernel_function_ty\n";
+
+ OS << " %p = OpFunctionParameter " << ArgTMapping->SPIRVTy << '\n';
+
+ for (size_t i = 0; i != CoordsNum; ++i)
+ OS << " %coords_param_" << CoordsNames[i].str()
+ << " = OpFunctionParameter %uint\n";
+
+ OS << " %11 = OpLabel\n"
+ " OpReturnValue %p\n"
+ " OpFunctionEnd\n";
+
+ return true;
+}
+
+} // namespace rs2spirv
diff --git a/rsov/compiler/ReflectionPass.h b/rsov/compiler/ReflectionPass.h
new file mode 100644
index 0000000..6770eaf
--- /dev/null
+++ b/rsov/compiler/ReflectionPass.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016, 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 RS_SPIRV_REFLECTION_PASS_H
+#define RS_SPIRV_REFLECTION_PASS_H
+
+#include "llvm/ADT/StringRef.h"
+
+#include <iosfwd>
+
+namespace llvm {
+class ModulePass;
+}
+
+namespace bcinfo {
+class MetadataExtractor;
+}
+
+namespace rs2spirv {
+
+llvm::ModulePass *createReflectionPass(std::ostream &OS,
+ bcinfo::MetadataExtractor &ME);
+
+} // namespace rs2spirv
+
+#endif
\ No newline at end of file
diff --git a/rsov/compiler/rs2spirv.cpp b/rsov/compiler/rs2spirv.cpp
new file mode 100644
index 0000000..ca3d956
--- /dev/null
+++ b/rsov/compiler/rs2spirv.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2016, 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 "RSSPIRVWriter.h"
+#include "llvm/Bitcode/ReaderWriter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/DataStream.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/SPIRV.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include "unit_tests/TestRunner.h"
+
+#include <fstream>
+#include <iterator>
+
+#define DEBUG_TYPE "rs2spirv"
+
+namespace kExt {
+const char SPIRVBinary[] = ".spv";
+}
+
+using namespace llvm;
+
+static cl::opt<std::string> InputFile(cl::Positional, cl::desc("<input file>"),
+ cl::init("-"));
+
+static cl::opt<std::string> OutputFile("o",
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string>
+ KernelFile("lk", cl::desc("File with a compute shader kernel"),
+ cl::value_desc("kernel.spt"));
+
+static cl::opt<std::string> WrapperFile(
+ "lw",
+ cl::desc("Generated wrapper file"
+ "(with entrypoint function and input/output images or buffers)"),
+ cl::value_desc("wrapper.spt"));
+
+static cl::opt<bool> IsPrintAsWords(
+ "print-as-words",
+ cl::desc("Print an input .spv file as a brace-init-list of words"),
+ cl::init(false));
+
+static cl::opt<bool>
+ IsRegularization("s",
+ cl::desc("Regularize LLVM to be representable by SPIR-V"));
+
+#ifdef RS2SPIRV_DEBUG
+static cl::opt<bool> RunTests("run-tests", cl::desc("Run unit tests"),
+ cl::init(false));
+#endif
+
+namespace SPIRV {
+extern bool SPIRVUseTextFormat;
+}
+
+static std::string removeExt(const std::string &FileName) {
+ size_t Pos = FileName.find_last_of(".");
+ if (Pos != std::string::npos)
+ return FileName.substr(0, Pos);
+ return FileName;
+}
+
+static int convertLLVMToSPIRV() {
+ if (!KernelFile.empty() && !WrapperFile.empty()) {
+ DEBUG(dbgs() << "Link " << KernelFile << " into " << WrapperFile << "\n");
+ if (!rs2spirv::Link(KernelFile, WrapperFile, OutputFile)) {
+ errs() << "Linking failed!\n\n";
+ return -1;
+ }
+ return 0;
+ }
+
+ LLVMContext Context;
+
+ std::string Err;
+ auto DS = getDataFileStreamer(InputFile, &Err);
+ if (!DS) {
+ errs() << "Fails to open input file: " << Err;
+ return -1;
+ }
+
+ ErrorOr<std::unique_ptr<Module>> MOrErr =
+ getStreamedBitcodeModule(InputFile, std::move(DS), Context);
+
+ if (std::error_code EC = MOrErr.getError()) {
+ errs() << "Fails to load bitcode: " << EC.message();
+ return -1;
+ }
+
+ std::unique_ptr<Module> M = std::move(*MOrErr);
+
+ if (std::error_code EC = M->materializeAll()) {
+ errs() << "Fails to materialize: " << EC.message();
+ return -1;
+ }
+
+ if (OutputFile.empty()) {
+ if (InputFile == "-")
+ OutputFile = "-";
+ else
+ OutputFile = removeExt(InputFile) + kExt::SPIRVBinary;
+ }
+
+ 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';
+ return -1;
+ }
+
+ return 0;
+}
+
+static int printAsWords() {
+ std::ifstream IFS(InputFile, std::ios::binary);
+ if (!IFS.good()) {
+ errs() << "Could not open input file\n";
+ return -1;
+ }
+
+ uint64_t FSize;
+ const auto EC = llvm::sys::fs::file_size(InputFile, FSize);
+ if (EC) {
+ errs() << "Fails to open input file: " << EC.message() << '\n';
+ return -1;
+ }
+
+ if (FSize % 4 != 0) {
+ errs() << "Input file is not a stream of words. Size mismatch.\n";
+ return -1;
+ }
+
+ std::istreambuf_iterator<char> It(IFS);
+ const std::istreambuf_iterator<char> End;
+
+ outs() << '{';
+
+ while (It != End) {
+ uint32_t val = 0;
+ // Mask the sign-extended values to prevent higher bits pollution.
+ val += uint32_t(*(It++)) & 0x000000FF;
+ val += (uint32_t(*(It++)) << 8) & 0x0000FF00;
+ val += (uint32_t(*(It++)) << 16) & 0x00FF0000;
+ val += (uint32_t(*(It++)) << 24) & 0xFF000000;
+ outs() << val << (It != End ? ", " : "};\n");
+ }
+
+ return 0;
+}
+
+int main(int ac, char **av) {
+ EnablePrettyStackTrace();
+ sys::PrintStackTraceOnErrorSignal(av[0]);
+ PrettyStackTraceProgram X(ac, av);
+
+ cl::ParseCommandLineOptions(ac, av, "RenderScript to SPIRV translator");
+
+#ifdef RS2SPIRV_DEBUG
+ if (RunTests)
+ return rs2spirv::TestRunnerContext::runTests();
+#endif
+
+ if (IsPrintAsWords)
+ return printAsWords();
+
+ return convertLLVMToSPIRV();
+}
diff --git a/rsov/compiler/rs2spirv_driver.sh b/rsov/compiler/rs2spirv_driver.sh
new file mode 100755
index 0000000..085812e
--- /dev/null
+++ b/rsov/compiler/rs2spirv_driver.sh
@@ -0,0 +1,48 @@
+# Copyright 2016, 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.
+
+#!/bin/bash
+
+if [ $# -lt 3 ]; then
+ echo 1>&2 "$0: not enough arguments"
+ echo 1>&2 $#
+ exit 2
+fi
+
+AND_HOME=$ANDROID_BUILD_TOP
+SPIRV_TOOLS_PATH=$1
+
+script_name="$2"
+script=${2%.*} # Remove extension.
+
+output_folder="$3"
+mkdir -p $output_folder
+
+eval llvm-rs-cc -o "$output_folder" -S -emit-llvm -Wall -Werror -target-api 24 \
+ -I "$AND_HOME/external/clang/lib/Headers" -I "$AND_HOME/frameworks/rs/scriptc" \
+ "$script_name"
+eval llvm-as "$output_folder/bc32/$script.ll" -o "$output_folder/$script.bc"
+eval rs2spirv "$output_folder/$script.bc" -o "$output_folder/$script.rs.spv" \
+ -wo "$output_folder/$script.w.spt" -debug
+eval "$SPIRV_TOOLS_PATH/spirv-dis" "$output_folder/$script.rs.spv" \
+ --no-color > "$output_folder/$script.rs.spt"
+eval rs2spirv -o "$output_folder/$script.spt" -lk "$output_folder/$script.rs.spt" \
+ -lw "$output_folder/$script.w.spt" -debug
+eval "$SPIRV_TOOLS_PATH/spirv-as" "$output_folder/$script.spt" \
+ -o "$output_folder/$script.spv"
+echo
+eval rs2spirv "$output_folder/$script.spv" -print-as-words
+echo
+eval "$SPIRV_TOOLS_PATH/spirv-val" "$output_folder/$script.spv"
+echo
diff --git a/rsov/compiler/tests/globals/mul.ll b/rsov/compiler/tests/globals/mul.ll
new file mode 100644
index 0000000..339f2bf
--- /dev/null
+++ b/rsov/compiler/tests/globals/mul.ll
@@ -0,0 +1,55 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: OpMemberDecorate %rs_linker_struct___GPUBuffer 0 Offset 0
+; CHECK: OpDecorate %rs_linker_struct___GPUBuffer BufferBlock
+; CHECK: OpDecorate %rs_linker___GPUBlock DescriptorSet 0
+; CHECK: OpDecorate %rs_linker___GPUBlock Binding 2
+
+; CHECK: %rs_linker_struct___GPUBuffer = OpTypeStruct %float
+; CHECK: OpTypePointer Uniform %rs_linker_struct___GPUBuffer
+
+; CHECK: %rs_linker___GPUBlock = OpVariable %{{.+}} Uniform
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpInBoundsPtrAccessChain
+
+@c1 = common global float 0.000000e+00, align 4
+
+; Function Attrs: norecurse nounwind readonly
+define <4 x float> @k1(<4 x float> %in) #0 {
+entry:
+ %0 = load float, float* @c1, align 4, !tbaa !11
+ %splat.splatinsert = insertelement <4 x float> undef, float %0, i32 0
+ %splat.splat = shufflevector <4 x float> %splat.splatinsert, <4 x float> undef, <4 x i32> zeroinitializer
+ %mul = fmul <4 x float> %splat.splat, %in
+ ret <4 x float> %mul
+}
+
+attributes #0 = { norecurse nounwind readonly "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_var = !{!6}
+!\23rs_object_slots = !{}
+!\23rs_export_foreach_name = !{!7, !8}
+!\23rs_export_foreach = !{!9, !10}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"c1", !"1"}
+!7 = !{!"root"}
+!8 = !{!"k1"}
+!9 = !{!"0"}
+!10 = !{!"35"}
+!11 = !{!12, !12, i64 0}
+!12 = !{!"float", !13, i64 0}
+!13 = !{!"omnipotent char", !14, i64 0}
+!14 = !{!"Simple C/C++ TBAA"}
diff --git a/rsov/compiler/tests/globals/mul.rs b/rsov/compiler/tests/globals/mul.rs
new file mode 100644
index 0000000..77c5c52
--- /dev/null
+++ b/rsov/compiler/tests/globals/mul.rs
@@ -0,0 +1,23 @@
+// Copyright (C) 2016 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+float c1;
+
+float4 __attribute__((kernel)) k1(float4 in) {
+ return c1 * in;
+}
diff --git a/rsov/compiler/tests/globals/mul2.ll b/rsov/compiler/tests/globals/mul2.ll
new file mode 100644
index 0000000..dcdd9d4
--- /dev/null
+++ b/rsov/compiler/tests/globals/mul2.ll
@@ -0,0 +1,67 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: OpMemberDecorate %rs_linker_struct___GPUBuffer 0 Offset 0
+; CHECK: OpMemberDecorate %rs_linker_struct___GPUBuffer 1 Offset 8
+; CHECK: OpMemberDecorate %rs_linker_struct___GPUBuffer 2 Offset 24
+; CHECK: OpMemberDecorate %rs_linker_struct___GPUBuffer 3 Offset 28
+; CHECK: OpDecorate %rs_linker_struct___GPUBuffer BufferBlock
+; CHECK: OpDecorate %rs_linker___GPUBlock DescriptorSet 0
+; CHECK: OpDecorate %rs_linker___GPUBlock Binding 2
+
+; CHECK: %rs_linker_struct___GPUBuffer = OpTypeStruct %float %v4float %uchar %uint
+; CHECK: OpTypePointer Uniform %rs_linker_struct___GPUBuffer
+
+; CHECK: %rs_linker___GPUBlock = OpVariable %{{.+}} Uniform
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpInBoundsPtrAccessChain
+
+@c1 = common global float 0.000000e+00, align 4
+@c2 = common global <4 x float> zeroinitializer, align 16
+@d = common global i8 0, align 1
+@i = common global i32 0, align 4
+
+; Function Attrs: norecurse nounwind readonly
+define <4 x float> @k1(<4 x float> %in) #0 {
+entry:
+ %0 = load float, float* @c1, align 4, !tbaa !14
+ %splat.splatinsert = insertelement <4 x float> undef, float %0, i32 0
+ %splat.splat = shufflevector <4 x float> %splat.splatinsert, <4 x float> undef, <4 x i32> zeroinitializer
+ %mul = fmul <4 x float> %splat.splat, %in
+ %1 = load <4 x float>, <4 x float>* @c2, align 16, !tbaa !18
+ %mul1 = fmul <4 x float> %1, %mul
+ ret <4 x float> %mul1
+}
+
+attributes #0 = { norecurse nounwind readonly "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_var = !{!6, !7, !8, !9}
+!\23rs_object_slots = !{}
+!\23rs_export_foreach_name = !{!10, !11}
+!\23rs_export_foreach = !{!12, !13}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"c1", !"1"}
+!7 = !{!"d", !"7"}
+!8 = !{!"c2", !"float4"}
+!9 = !{!"i", !"5"}
+!10 = !{!"root"}
+!11 = !{!"k1"}
+!12 = !{!"0"}
+!13 = !{!"35"}
+!14 = !{!15, !15, i64 0}
+!15 = !{!"float", !16, i64 0}
+!16 = !{!"omnipotent char", !17, i64 0}
+!17 = !{!"Simple C/C++ TBAA"}
+!18 = !{!16, !16, i64 0}
diff --git a/rsov/compiler/tests/globals/mul2.rs b/rsov/compiler/tests/globals/mul2.rs
new file mode 100644
index 0000000..78e67ac
--- /dev/null
+++ b/rsov/compiler/tests/globals/mul2.rs
@@ -0,0 +1,26 @@
+// Copyright (C) 2016 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+float c1;
+uchar d;
+float4 c2;
+int i;
+
+float4 __attribute__((kernel)) k1(float4 in) {
+ return c1 * in * c2;
+}
diff --git a/rsov/compiler/tests/image/blend.ll b/rsov/compiler/tests/image/blend.ll
new file mode 100644
index 0000000..e9ac0cb
--- /dev/null
+++ b/rsov/compiler/tests/image/blend.ll
@@ -0,0 +1,74 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: OpMemberDecorate %rs_linker_struct___GPUBuffer 0 Offset 0
+; CHECK: OpDecorate %rs_linker_struct___GPUBuffer BufferBlock
+; CHECK: OpDecorate %rs_linker___GPUBlock DescriptorSet 0
+; CHECK: OpDecorate %rs_linker___GPUBlock Binding 2
+
+; CHECK: %rs_linker_struct___GPUBuffer = OpTypeStruct %uchar
+; CHECK: OpTypePointer Uniform %rs_linker_struct___GPUBuffer
+
+; CHECK: %rs_linker___GPUBlock = OpVariable %{{.+}} Uniform
+
+; CHECK-NOT: %rs_linker__Z14convert_uchar4Dv4_j = OpFunction %v4uchar
+; CHECK-NOT: %rs_linker__Z13convert_uint4Dv4_h = OpFunction %v4uint
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpFunctionCall
+
+; CHECK: OpUConvert %v4uint
+; CHECK: OpUConvert %v4uchar
+
+@alpha = global i8 0, align 1
+
+; Function Attrs: nounwind readonly
+define <4 x i8> @setImageAlpha(<4 x i8> %in, i32 %x, i32 %y) #0 {
+entry:
+ %call = tail call <4 x i32> @_Z13convert_uint4Dv4_h(<4 x i8> %in) #2
+ %0 = load i8, i8* @alpha, align 1, !tbaa !11
+ %conv = zext i8 %0 to i32
+ %splat.splatinsert = insertelement <4 x i32> undef, i32 %conv, i32 0
+ %splat.splat = shufflevector <4 x i32> %splat.splatinsert, <4 x i32> undef, <4 x i32> zeroinitializer
+ %mul = mul <4 x i32> %splat.splat, %call
+ %shr = lshr <4 x i32> %mul, <i32 8, i32 8, i32 8, i32 8>
+ %call1 = tail call <4 x i8> @_Z14convert_uchar4Dv4_j(<4 x i32> %shr) #2
+ %1 = insertelement <4 x i8> %call1, i8 %0, i32 3
+ ret <4 x i8> %1
+}
+
+; Function Attrs: nounwind readnone
+declare <4 x i8> @_Z14convert_uchar4Dv4_j(<4 x i32>) #1
+
+; Function Attrs: nounwind readnone
+declare <4 x i32> @_Z13convert_uint4Dv4_h(<4 x i8>) #1
+
+attributes #0 = { nounwind readonly "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind readnone }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_var = !{!6}
+!\23rs_object_slots = !{}
+!\23rs_export_foreach_name = !{!7, !8}
+!\23rs_export_foreach = !{!9, !10}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"alpha", !"7"}
+!7 = !{!"root"}
+!8 = !{!"setImageAlpha"}
+!9 = !{!"0"}
+!10 = !{!"59"}
+!11 = !{!12, !12, i64 0}
+!12 = !{!"omnipotent char", !13, i64 0}
+!13 = !{!"Simple C/C++ TBAA"}
diff --git a/rsov/compiler/tests/image/blend.rs b/rsov/compiler/tests/image/blend.rs
new file mode 100644
index 0000000..ea1d323
--- /dev/null
+++ b/rsov/compiler/tests/image/blend.rs
@@ -0,0 +1,26 @@
+// Copyright (C) 2011 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+uchar alpha = 0x0;
+
+uchar4 __attribute__((kernel)) setImageAlpha(uchar4 in, uint32_t x, uint32_t y) {
+ uchar4 out;
+ out.rgba = convert_uchar4((convert_uint4(in.rgba) * alpha) >> (uint4)8);
+ out.a = alpha;
+ return out;
+}
diff --git a/rsov/compiler/tests/image/contrast.ll b/rsov/compiler/tests/image/contrast.ll
new file mode 100644
index 0000000..1e77c5d
--- /dev/null
+++ b/rsov/compiler/tests/image/contrast.ll
@@ -0,0 +1,109 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: %rs_linker_struct___GPUBuffer = OpTypeStruct %float %float
+; CHECK: OpTypePointer Uniform %rs_linker_struct___GPUBuffer
+
+; CHECK: %rs_linker___GPUBlock = OpVariable %{{.+}} Uniform
+
+; CHECK-NOT: %rs_linker__Z14convert_float3Dv3_h = OpFunction %v3float
+; CHECK-NOT: %rs_linker__Z14convert_uchar3Dv3_f = OpFunction %v3uchar
+; CHECK-NOT: %rs_linker__Z5clampDv3_fff = OpFunction %v3float
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpFunctionCall
+
+; CHECK: OpConvertUToF %v3float
+; CHECK: OpExtInst %v3float %glsl_ext_ins FClamp
+; CHECK: OpConvertFToU %v3uchar
+
+; Undef value representation.
+; CHECK-NOT: 4294967295
+
+@brightM = internal unnamed_addr global float 0.000000e+00, align 4
+@brightC = internal unnamed_addr global float 0.000000e+00, align 4
+
+; Function Attrs: nounwind
+define void @setBright(float %v) #0 {
+entry:
+ %div = fdiv float %v, 1.000000e+02
+ %call = tail call float @_Z3powff(float 2.000000e+00, float %div) #4
+ store float %call, float* @brightM, align 4, !tbaa !11
+ %mul = fmul float %call, 1.270000e+02
+ %sub = fsub float 1.270000e+02, %mul
+ store float %sub, float* @brightC, align 4, !tbaa !11
+ ret void
+}
+
+; Function Attrs: nounwind readnone
+declare float @_Z3powff(float, float) #1
+
+; Function Attrs: nounwind readonly
+define <4 x i8> @contrast(<4 x i8> %in) #2 {
+entry:
+ %0 = shufflevector <4 x i8> %in, <4 x i8> undef, <3 x i32> <i32 0, i32 1, i32 2>
+ %call = tail call <3 x float> @_Z14convert_float3Dv3_h(<3 x i8> %0) #4
+ %1 = load float, float* @brightM, align 4, !tbaa !11
+ %splat.splatinsert = insertelement <3 x float> undef, float %1, i32 0
+ %splat.splat = shufflevector <3 x float> %splat.splatinsert, <3 x float> undef, <3 x i32> zeroinitializer
+ %mul = fmul <3 x float> %call, %splat.splat
+ %2 = load float, float* @brightC, align 4, !tbaa !11
+ %splat.splatinsert1 = insertelement <3 x float> undef, float %2, i32 0
+ %splat.splat2 = shufflevector <3 x float> %splat.splatinsert1, <3 x float> undef, <3 x i32> zeroinitializer
+ %add = fadd <3 x float> %mul, %splat.splat2
+ %call4 = tail call <3 x float> @_Z5clampDv3_fff(<3 x float> %add, float 0.000000e+00, float 2.550000e+02) #4
+ %call5 = tail call <3 x i8> @_Z14convert_uchar3Dv3_f(<3 x float> %call4) #4
+ %3 = shufflevector <3 x i8> %call5, <3 x i8> undef, <4 x i32> <i32 0, i32 1, i32 2, i32 undef>
+ %4 = insertelement <4 x i8> %3, i8 -1, i32 3
+ ret <4 x i8> %4
+}
+
+; Function Attrs: nounwind readnone
+declare <3 x float> @_Z14convert_float3Dv3_h(<3 x i8>) #1
+
+; Function Attrs: nounwind readnone
+declare <3 x i8> @_Z14convert_uchar3Dv3_f(<3 x float>) #1
+
+; Function Attrs: nounwind readnone
+declare <3 x float> @_Z5clampDv3_fff(<3 x float>, float, float) #1
+
+; Function Attrs: noinline nounwind
+define void @.helper_setBright({ float }* nocapture) #3 {
+entry:
+ %1 = getelementptr inbounds { float }, { float }* %0, i32 0, i32 0
+ %2 = load float, float* %1, align 4
+ tail call void @setBright(float %2)
+ ret void
+}
+
+attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind readonly "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #3 = { noinline nounwind }
+attributes #4 = { nounwind readnone }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_func = !{!6}
+!\23rs_export_foreach_name = !{!7, !8}
+!\23rs_export_foreach = !{!9, !10}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!".helper_setBright"}
+!7 = !{!"root"}
+!8 = !{!"contrast"}
+!9 = !{!"0"}
+!10 = !{!"35"}
+!11 = !{!12, !12, i64 0}
+!12 = !{!"float", !13, i64 0}
+!13 = !{!"omnipotent char", !14, i64 0}
+!14 = !{!"Simple C/C++ TBAA"}
diff --git a/rsov/compiler/tests/image/contrast.rs b/rsov/compiler/tests/image/contrast.rs
new file mode 100644
index 0000000..dd95ad3
--- /dev/null
+++ b/rsov/compiler/tests/image/contrast.rs
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+static float brightM = 0.f;
+static float brightC = 0.f;
+
+void setBright(float v) {
+ brightM = pow(2.f, v / 100.f);
+ brightC = 127.f - brightM * 127.f;
+}
+
+uchar4 __attribute__((kernel)) contrast(uchar4 in) {
+ float3 v = convert_float3(in.rgb) * brightM + brightC;
+ uchar4 o;
+ o.rgb = convert_uchar3(clamp(v, 0.f, 255.f));
+ o.a = 0xff;
+ return o;
+}
\ No newline at end of file
diff --git a/rsov/compiler/tests/image/copy.ll b/rsov/compiler/tests/image/copy.ll
new file mode 100644
index 0000000..ea5226f
--- /dev/null
+++ b/rsov/compiler/tests/image/copy.ll
@@ -0,0 +1,31 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: %main = OpFunction
+
+; Function Attrs: norecurse nounwind readnone
+define <4 x i8> @copy(<4 x i8> %v_in) #0 {
+entry:
+ ret <4 x i8> %v_in
+}
+
+attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_foreach_name = !{!6, !7}
+!\23rs_export_foreach = !{!8, !9}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"root"}
+!7 = !{!"copy"}
+!8 = !{!"0"}
+!9 = !{!"35"}
diff --git a/rsov/compiler/tests/image/copy.rs b/rsov/compiler/tests/image/copy.rs
new file mode 100644
index 0000000..1f3ae2e
--- /dev/null
+++ b/rsov/compiler/tests/image/copy.rs
@@ -0,0 +1,7 @@
+#pragma version(1)
+#pragma rs java_package_name(rs2spirv)
+#pragma rs_fp_relaxed
+
+uchar4 __attribute__((kernel)) copy(uchar4 v_in) {
+ return v_in;
+}
diff --git a/rsov/compiler/tests/lit.cfg b/rsov/compiler/tests/lit.cfg
new file mode 100644
index 0000000..d9ab794
--- /dev/null
+++ b/rsov/compiler/tests/lit.cfg
@@ -0,0 +1,56 @@
+# -*- Python -*-
+
+# Configuration file for the 'lit' test runner.
+
+import re
+
+# name: The name of this test suite.
+config.name = 'rs2spirv'
+
+# suffixes: A list of file extensions to treat as test files.
+config.suffixes = ['.ll']
+
+# testFormat: The test format to use to interpret tests.
+import lit.formats
+config.test_format = lit.formats.ShTest()
+
+ANDROID_HOST_OUT = os.getenv("ANDROID_HOST_OUT")
+ANDROID_PRODUCT_OUT = os.getenv("ANDROID_PRODUCT_OUT")
+
+if not ANDROID_HOST_OUT or not ANDROID_PRODUCT_OUT:
+ import sys
+ sys.exit(1)
+
+# test_source_root: The path where tests are located (default is the test suite
+# root).
+config.test_source_root = None
+config.test_exec_root = os.path.join(ANDROID_HOST_OUT, 'tests', 'rs2spirv')
+
+tools_dir = os.pathsep.join([os.path.join(ANDROID_HOST_OUT, 'bin'),
+ os.path.join(ANDROID_HOST_OUT, 'lib64'),
+ os.path.join(ANDROID_PRODUCT_OUT, 'system/lib')])
+
+# Based on LLVM's lit.cfg: "For each occurrence of an llvm tool name
+# as its own word, replace it with the full path to the build directory
+# holding that tool."
+for pattern in [r"\bFileCheck\b",
+ r"\bllvm-as\b",
+ r"\bllvm-dis\b",
+ r"\bllvm-spirv\b",
+ r"\brs2spirv\b",
+ r"\bspirv-as\b",
+ r"\bspirv-dis\b",
+ r"\bspirv-val\b",
+ r"\brs2spirv\b",
+ r"\brs2spirv_lit_driver.sh\b",
+ r"\bopt\b"]:
+ tool_match = re.match(r"^(\\)?((\| )?)\W+b([\.0-9A-Za-z-_]+)\\b\W*$",
+ pattern)
+ tool_pipe = tool_match.group(2)
+ tool_name = tool_match.group(4)
+ import lit.util
+ tool_path = lit.util.which(tool_name, tools_dir)
+ if not tool_path:
+ lit_config.note("Did not find " + tool_name + " in " + tools_dir)
+ tool_path = os.path.join(tools_dir, tool_name)
+ config.substitutions.append((pattern, tool_pipe + tool_path))
diff --git a/rsov/compiler/tests/llvm-lit b/rsov/compiler/tests/llvm-lit
new file mode 100755
index 0000000..cc6f58e
--- /dev/null
+++ b/rsov/compiler/tests/llvm-lit
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+import os
+import sys
+
+# In the Android tree, use the environment variables set by envsetup.sh
+# to determine correct path for the root of the source tree.
+# TODO: To run clang tests, @LLVM_BINARY_DIR@ must be substituted also.
+android_source_root = os.getenv('ANDROID_BUILD_TOP', ".")
+llvm_source_root = os.path.join(android_source_root, 'external', 'llvm')
+
+# Make sure we can find the lit package.
+sys.path.append(os.path.join(llvm_source_root, 'utils', 'lit'))
+
+# Set up some builtin parameters, so that by default the LLVM test suite
+# configuration file knows how to find the object tree.
+builtin_parameters = {
+ 'llvm_site_config' : os.path.join('./', 'lit.site.cfg')
+}
+
+if __name__=='__main__':
+ import lit
+ lit.main(builtin_parameters)
diff --git a/rsov/compiler/tests/multi_function/blend_mf.ll b/rsov/compiler/tests/multi_function/blend_mf.ll
new file mode 100644
index 0000000..fe65043
--- /dev/null
+++ b/rsov/compiler/tests/multi_function/blend_mf.ll
@@ -0,0 +1,70 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK-NOT: %rs_linker__Z14convert_uchar4Dv4_j = OpFunction %v4uchar
+; CHECK-NOT: %rs_linker__Z13convert_uint4Dv4_h = OpFunction %v4uint
+; CHECK-NOT: OpFunction %v4uchar
+; CHECK-NOT: OpFunction %uchar
+
+; CHECK: %main = OpFunction
+
+; CHECK: OpUConvert %v4uint
+; CHECK: OpUConvert %v4uchar
+
+; CHECK-NOT: OpFunctionCall
+
+; Function Attrs: norecurse nounwind readnone
+define void @NOP() #0 {
+entry:
+ ret void
+}
+
+; Function Attrs: nounwind readnone
+define <4 x i8> @setImageAlpha(<4 x i8> %in) #1 {
+entry:
+ %call = tail call <4 x i32> @_Z13convert_uint4Dv4_h(<4 x i8> %in) #2
+ %mul = mul <4 x i32> %call, <i32 37, i32 37, i32 37, i32 37>
+ %shr = lshr <4 x i32> %mul, <i32 8, i32 8, i32 8, i32 8>
+ %call1 = tail call <4 x i8> @_Z14convert_uchar4Dv4_j(<4 x i32> %shr) #2
+ %call2 = tail call fastcc <4 x i8> @twice(<4 x i8> %call1)
+ %0 = insertelement <4 x i8> %call2, i8 37, i32 3
+ ret <4 x i8> %0
+}
+
+; Function Attrs: norecurse nounwind readnone
+define internal fastcc <4 x i8> @twice(<4 x i8> %in) #0 {
+entry:
+ %mul = shl <4 x i8> %in, <i8 1, i8 1, i8 1, i8 1>
+ ret <4 x i8> %mul
+}
+
+; Function Attrs: nounwind readnone
+declare <4 x i8> @_Z14convert_uchar4Dv4_j(<4 x i32>) #1
+
+; Function Attrs: nounwind readnone
+declare <4 x i32> @_Z13convert_uint4Dv4_h(<4 x i8>) #1
+
+attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind readnone }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_func = !{!6}
+!\23rs_export_foreach_name = !{!7, !8}
+!\23rs_export_foreach = !{!9, !10}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"NOP"}
+!7 = !{!"root"}
+!8 = !{!"setImageAlpha"}
+!9 = !{!"0"}
+!10 = !{!"35"}
diff --git a/rsov/compiler/tests/multi_function/blend_mf.rs b/rsov/compiler/tests/multi_function/blend_mf.rs
new file mode 100644
index 0000000..1769208
--- /dev/null
+++ b/rsov/compiler/tests/multi_function/blend_mf.rs
@@ -0,0 +1,38 @@
+// Copyright (C) 2011 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+static uchar4 twice(uchar4 in) {
+ return in * 2;
+}
+
+static uchar getConstant() {
+ return 37;
+}
+
+void NOP() {
+ uchar3 x = {1, 2, 3};
+ (void) convert_float3(x);
+}
+
+uchar4 __attribute__((kernel)) setImageAlpha(uchar4 in) {
+ uchar4 out;
+ out.rgba = twice(convert_uchar4((convert_uint4(in.rgba) * 37) >> (uint4)8));
+ out.a = getConstant();
+ NOP();
+ return out;
+}
diff --git a/rsov/compiler/tests/rs2spirv_lit_driver.sh b/rsov/compiler/tests/rs2spirv_lit_driver.sh
new file mode 100755
index 0000000..d084c90
--- /dev/null
+++ b/rsov/compiler/tests/rs2spirv_lit_driver.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Copyright 2016, 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.
+
+# TODO: Consider rewriting as a native binary instead of shell script.
+
+if [ $# -lt 1 ]; then
+ echo 1>&2 "$0: not enough arguments"
+ echo 1>&2 $#
+ exit 2
+fi
+
+script_path="$1"
+script_name=$(basename $script_path)
+script=${script_name%.*} # Remove extension.
+
+output_folder="driver_out"
+mkdir -p $output_folder
+
+eval llvm-as "$script_path" -o "$output_folder/$script.bc"
+eval rs2spirv "$output_folder/$script.bc" -o "$output_folder/$script.rs.spv" \
+ -wo "$output_folder/$script.w.spt"
+eval spirv-dis "$output_folder/$script.rs.spv" \
+ --no-color > "$output_folder/$script.rs.spt"
+eval rs2spirv -o "$output_folder/$script.spt" -lk "$output_folder/$script.rs.spt" \
+ -lw "$output_folder/$script.w.spt"
+eval spirv-as "$output_folder/$script.spt" \
+ -o "$output_folder/$script.spv"
+
+eval spirv-val "$output_folder/$script.spv"
+eval cat "$output_folder/$script.spt"
+
+eval rm "$output_folder/$script.*"
diff --git a/rsov/compiler/tests/rs_allocation/access_same.ll b/rsov/compiler/tests/rs_allocation/access_same.ll
new file mode 100644
index 0000000..7b2b25f
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/access_same.ll
@@ -0,0 +1,93 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: %rs_linker_struct___GPUBuffer = OpTypeStruct %uchar
+; CHECK: OpTypePointer Uniform %rs_linker_struct___GPUBuffer
+
+; CHECK: %rs_linker___GPUBlock = OpVariable %{{.+}} Uniform
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpFunctionCall
+
+; CHECK: OpImageRead %v4uchar
+; CHECK: OpImageRead %v4uchar %[[RS_ALLOC:.*]] %{{.*}}
+; CHECK: OpImageWrite %{{.*}}[[RS_ALLOC]] %{{.*}} %{{.*}}
+; CHECK: OpImageWrite
+
+
+%struct.rs_allocation = type { i32* }
+
+@a1 = common global %struct.rs_allocation zeroinitializer, align 4
+@c1 = common global i8 0, align 1
+
+; Function Attrs: nounwind
+define <4 x i8> @k1(<4 x i8> %in) #0 {
+entry:
+ %0 = extractelement <4 x i8> %in, i32 0
+ %conv = zext i8 %0 to i32
+ %1 = extractelement <4 x i8> %in, i32 1
+ %conv1 = zext i8 %1 to i32
+ %.unpack = load i32, i32* bitcast (%struct.rs_allocation* @a1 to i32*), align 4
+ %2 = insertvalue [1 x i32] undef, i32 %.unpack, 0
+ %call = tail call <4 x i8> @_Z21rsGetElementAt_uchar413rs_allocationjj([1 x i32] %2, i32 %conv, i32 %conv1) #2
+ %3 = extractelement <4 x i8> %in, i32 2
+ %conv2 = zext i8 %3 to i32
+ %4 = extractelement <4 x i8> %in, i32 3
+ %conv3 = zext i8 %4 to i32
+ %.unpack14 = load i32, i32* bitcast (%struct.rs_allocation* @a1 to i32*), align 4
+ %5 = insertvalue [1 x i32] undef, i32 %.unpack14, 0
+ tail call void @_Z21rsSetElementAt_uchar413rs_allocationDv4_hjj([1 x i32] %5, <4 x i8> %call, i32 %conv2, i32 %conv3) #2
+ %6 = load i8, i8* @c1, align 1, !tbaa !13
+ %conv4 = zext i8 %6 to i32
+ %7 = extractelement <4 x i8> %call, i32 2
+ %conv5 = zext i8 %7 to i32
+ %add = add nuw nsw i32 %conv4, %conv5
+ %conv6 = trunc i32 %add to i8
+ %8 = insertelement <4 x i8> %call, i8 %conv6, i32 2
+ ret <4 x i8> %8
+}
+
+declare <4 x i8> @_Z21rsGetElementAt_uchar413rs_allocationjj([1 x i32], i32, i32) #1
+
+declare void @_Z21rsSetElementAt_uchar413rs_allocationDv4_hjj([1 x i32], <4 x i8>, i32, i32) #1
+
+; Function Attrs: nounwind
+define void @.rs.dtor() #0 {
+entry:
+ tail call void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation* nonnull @a1) #2
+ ret void
+}
+
+declare void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation*) #1
+
+attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_var = !{!6, !7}
+!\23rs_object_slots = !{!8}
+!\23rs_export_foreach_name = !{!9, !10}
+!\23rs_export_foreach = !{!11, !12}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"c1", !"7"}
+!7 = !{!"a1", !"20"}
+!8 = !{!"1"}
+!9 = !{!"root"}
+!10 = !{!"k1"}
+!11 = !{!"0"}
+!12 = !{!"35"}
+!13 = !{!14, !14, i64 0}
+!14 = !{!"omnipotent char", !15, i64 0}
+!15 = !{!"Simple C/C++ TBAA"}
diff --git a/rsov/compiler/tests/rs_allocation/access_same.rs b/rsov/compiler/tests/rs_allocation/access_same.rs
new file mode 100644
index 0000000..7fce3bb
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/access_same.rs
@@ -0,0 +1,28 @@
+// Copyright (C) 2016 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+uchar c1;
+rs_allocation a1;
+
+uchar4 __attribute__((kernel)) k1(uchar4 in) {
+ uchar4 res = rsGetElementAt_uchar4(a1, in.r, in.g);
+ rsSetElementAt_uchar4(a1, res, in.b, in.a);
+ res.b += c1;
+ return res;
+}
+
diff --git a/rsov/compiler/tests/rs_allocation/copy_coords.ll b/rsov/compiler/tests/rs_allocation/copy_coords.ll
new file mode 100644
index 0000000..bed18d5
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/copy_coords.ll
@@ -0,0 +1,40 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpFunctionCall
+
+; CHECK: %coords_load = OpLoad %v3uint %global_invocation_id
+; CHECK: %coords_x = OpCompositeExtract %uint %coords_load 0
+; CHECK: %coords_y = OpCompositeExtract %uint %coords_load 1
+
+; Function Attrs: norecurse nounwind readnone
+define <4 x i8> @copy_coords(<4 x i8> %in, i32 %x, i32 %y) #0 {
+entry:
+ %conv = trunc i32 %x to i8
+ %0 = insertelement <4 x i8> %in, i8 %conv, i32 0
+ %conv1 = trunc i32 %y to i8
+ %1 = insertelement <4 x i8> %0, i8 %conv1, i32 1
+ ret <4 x i8> %1
+}
+
+attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_foreach_name = !{!6, !7}
+!\23rs_export_foreach = !{!8, !9}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"root"}
+!7 = !{!"copy_coords"}
+!8 = !{!"0"}
+!9 = !{!"59"}
diff --git a/rsov/compiler/tests/rs_allocation/copy_coords.rs b/rsov/compiler/tests/rs_allocation/copy_coords.rs
new file mode 100644
index 0000000..2b36ee7
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/copy_coords.rs
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+uchar4 __attribute__((kernel)) copy_coords(uchar4 in, uint32_t x, uint32_t y) {
+ uchar4 res = in;
+ res.r = (uchar) x;
+ res.g = (uchar) y;
+ return res;
+}
diff --git a/rsov/compiler/tests/rs_allocation/multi_read.ll b/rsov/compiler/tests/rs_allocation/multi_read.ll
new file mode 100644
index 0000000..a405a63
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/multi_read.ll
@@ -0,0 +1,95 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: %rs_linker_struct___GPUBuffer = OpTypeStruct %uchar
+; CHECK: OpTypePointer Uniform %rs_linker_struct___GPUBuffer
+
+; CHECK: %rs_linker___GPUBlock = OpVariable %{{.+}} Uniform
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpFunctionCall
+
+; CHECK: OpImageRead %v4uchar
+; CHECK: OpImageRead %v4uchar
+; CHECK: OpImageRead %v4uchar
+; CHECK: OpImageWrite
+
+%struct.rs_allocation = type { i32* }
+
+@a1 = common global %struct.rs_allocation zeroinitializer, align 4
+@a2 = common global %struct.rs_allocation zeroinitializer, align 4
+@c1 = common global i8 0, align 1
+
+; Function Attrs: nounwind
+define <4 x i8> @k1(<4 x i8> %in) #0 {
+entry:
+ %0 = extractelement <4 x i8> %in, i32 0
+ %conv = zext i8 %0 to i32
+ %1 = extractelement <4 x i8> %in, i32 1
+ %conv1 = zext i8 %1 to i32
+ %.unpack = load i32, i32* bitcast (%struct.rs_allocation* @a1 to i32*), align 4
+ %2 = insertvalue [1 x i32] undef, i32 %.unpack, 0
+ %call = tail call <4 x i8> @_Z21rsGetElementAt_uchar413rs_allocationjj([1 x i32] %2, i32 %conv, i32 %conv1) #2
+ %3 = extractelement <4 x i8> %in, i32 2
+ %conv2 = zext i8 %3 to i32
+ %4 = extractelement <4 x i8> %in, i32 3
+ %conv3 = zext i8 %4 to i32
+ %.unpack16 = load i32, i32* bitcast (%struct.rs_allocation* @a2 to i32*), align 4
+ %5 = insertvalue [1 x i32] undef, i32 %.unpack16, 0
+ %call4 = tail call <4 x i8> @_Z21rsGetElementAt_uchar413rs_allocationjj([1 x i32] %5, i32 %conv2, i32 %conv3) #2
+ %add = add <4 x i8> %call4, %call
+ %6 = load i8, i8* @c1, align 1, !tbaa !15
+ %conv5 = zext i8 %6 to i32
+ %7 = extractelement <4 x i8> %add, i32 2
+ %conv6 = zext i8 %7 to i32
+ %add7 = add nuw nsw i32 %conv6, %conv5
+ %conv8 = trunc i32 %add7 to i8
+ %8 = insertelement <4 x i8> %add, i8 %conv8, i32 2
+ ret <4 x i8> %8
+}
+
+declare <4 x i8> @_Z21rsGetElementAt_uchar413rs_allocationjj([1 x i32], i32, i32) #1
+
+; Function Attrs: nounwind
+define void @.rs.dtor() #0 {
+entry:
+ tail call void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation* nonnull @a1) #2
+ tail call void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation* nonnull @a2) #2
+ ret void
+}
+
+declare void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation*) #1
+
+attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_var = !{!6, !7, !8}
+!\23rs_object_slots = !{!9, !10}
+!\23rs_export_foreach_name = !{!11, !12}
+!\23rs_export_foreach = !{!13, !14}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"c1", !"7"}
+!7 = !{!"a1", !"20"}
+!8 = !{!"a2", !"20"}
+!9 = !{!"1"}
+!10 = !{!"2"}
+!11 = !{!"root"}
+!12 = !{!"k1"}
+!13 = !{!"0"}
+!14 = !{!"35"}
+!15 = !{!16, !16, i64 0}
+!16 = !{!"omnipotent char", !17, i64 0}
+!17 = !{!"Simple C/C++ TBAA"}
diff --git a/rsov/compiler/tests/rs_allocation/multi_read.rs b/rsov/compiler/tests/rs_allocation/multi_read.rs
new file mode 100644
index 0000000..bebb0f9
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/multi_read.rs
@@ -0,0 +1,29 @@
+// Copyright (C) 2016 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+uchar c1;
+rs_allocation a1;
+rs_allocation a2;
+
+uchar4 __attribute__((kernel)) k1(uchar4 in) {
+ uchar4 res = rsGetElementAt_uchar4(a1, in.r, in.g);
+ res += rsGetElementAt_uchar4(a2, in.b, in.a);
+ res.b += c1;
+ return res;
+}
+
diff --git a/rsov/compiler/tests/rs_allocation/read.ll b/rsov/compiler/tests/rs_allocation/read.ll
new file mode 100644
index 0000000..623b981
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/read.ll
@@ -0,0 +1,82 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: %rs_linker_struct___GPUBuffer = OpTypeStruct %uchar
+; CHECK: OpTypePointer Uniform %rs_linker_struct___GPUBuffer
+
+; CHECK: %rs_linker___GPUBlock = OpVariable %{{.+}} Uniform
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpFunctionCall
+
+; CHECK: OpImageRead %v4uchar
+; CHECK: OpImageRead %v4uchar
+; CHECK: OpImageWrite
+
+%struct.rs_allocation = type { i32* }
+
+@alloc = common global %struct.rs_allocation zeroinitializer, align 4
+@c1 = common global i8 0, align 1
+
+; Function Attrs: nounwind
+define <4 x i8> @k1(<4 x i8> %in) #0 {
+entry:
+ %0 = extractelement <4 x i8> %in, i32 0
+ %conv = zext i8 %0 to i32
+ %1 = extractelement <4 x i8> %in, i32 1
+ %conv1 = zext i8 %1 to i32
+ %.unpack = load i32, i32* bitcast (%struct.rs_allocation* @alloc to i32*), align 4
+ %2 = insertvalue [1 x i32] undef, i32 %.unpack, 0
+ %call = tail call <4 x i8> @_Z21rsGetElementAt_uchar413rs_allocationjj([1 x i32] %2, i32 %conv, i32 %conv1) #2
+ %3 = load i8, i8* @c1, align 1, !tbaa !13
+ %conv2 = zext i8 %3 to i32
+ %4 = extractelement <4 x i8> %call, i32 2
+ %conv3 = zext i8 %4 to i32
+ %add = add nuw nsw i32 %conv3, %conv2
+ %conv4 = trunc i32 %add to i8
+ %5 = insertelement <4 x i8> %call, i8 %conv4, i32 2
+ ret <4 x i8> %5
+}
+
+declare <4 x i8> @_Z21rsGetElementAt_uchar413rs_allocationjj([1 x i32], i32, i32) #1
+
+; Function Attrs: nounwind
+define void @.rs.dtor() #0 {
+entry:
+ tail call void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation* nonnull @alloc) #2
+ ret void
+}
+
+declare void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation*) #1
+
+attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_var = !{!6, !7}
+!\23rs_object_slots = !{!8}
+!\23rs_export_foreach_name = !{!9, !10}
+!\23rs_export_foreach = !{!11, !12}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"c1", !"7"}
+!7 = !{!"alloc", !"20"}
+!8 = !{!"1"}
+!9 = !{!"root"}
+!10 = !{!"k1"}
+!11 = !{!"0"}
+!12 = !{!"35"}
+!13 = !{!14, !14, i64 0}
+!14 = !{!"omnipotent char", !15, i64 0}
+!15 = !{!"Simple C/C++ TBAA"}
diff --git a/rsov/compiler/tests/rs_allocation/read.rs b/rsov/compiler/tests/rs_allocation/read.rs
new file mode 100644
index 0000000..0cde99f
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/read.rs
@@ -0,0 +1,26 @@
+// Copyright (C) 2016 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+uchar c1;
+rs_allocation alloc;
+
+uchar4 __attribute__((kernel)) k1(uchar4 in) {
+ uchar4 res = rsGetElementAt_uchar4(alloc, in.r, in.g);
+ res.b += c1;
+ return res;
+}
diff --git a/rsov/compiler/tests/rs_allocation/read_write.ll b/rsov/compiler/tests/rs_allocation/read_write.ll
new file mode 100644
index 0000000..2ea6e46
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/read_write.ll
@@ -0,0 +1,85 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: %rs_linker_struct___GPUBuffer = OpTypeStruct %uchar
+; CHECK: OpTypePointer Uniform %rs_linker_struct___GPUBuffer
+
+; CHECK: %rs_linker___GPUBlock = OpVariable %{{.+}} Uniform
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpFunctionCall
+
+; CHECK: OpImageRead %v4uchar
+; CHECK: OpImageRead %v4uchar
+; CHECK: OpImageWrite
+
+%struct.rs_allocation = type { i32* }
+
+@r = common global %struct.rs_allocation zeroinitializer, align 4
+@w = common global %struct.rs_allocation zeroinitializer, align 4
+@c1 = common global i8 0, align 1
+
+; Function Attrs: nounwind
+define <4 x i8> @k1(<4 x i8> %in, i32 %x, i32 %y) #0 {
+entry:
+ %.unpack = load i32, i32* bitcast (%struct.rs_allocation* @r to i32*), align 4
+ %0 = insertvalue [1 x i32] undef, i32 %.unpack, 0
+ %call = tail call <4 x i8> @_Z21rsGetElementAt_uchar413rs_allocationjj([1 x i32] %0, i32 %x, i32 %y) #2
+ %1 = load i8, i8* @c1, align 1, !tbaa !15
+ %splat.splatinsert = insertelement <4 x i8> undef, i8 %1, i32 0
+ %splat.splat = shufflevector <4 x i8> %splat.splatinsert, <4 x i8> undef, <4 x i32> zeroinitializer
+ %add = add <4 x i8> %call, %in
+ %add1 = add <4 x i8> %add, %splat.splat
+ %.unpack6 = load i32, i32* bitcast (%struct.rs_allocation* @w to i32*), align 4
+ %2 = insertvalue [1 x i32] undef, i32 %.unpack6, 0
+ tail call void @_Z21rsSetElementAt_uchar413rs_allocationDv4_hjj([1 x i32] %2, <4 x i8> %add1, i32 %x, i32 %y) #2
+ ret <4 x i8> %in
+}
+
+declare <4 x i8> @_Z21rsGetElementAt_uchar413rs_allocationjj([1 x i32], i32, i32) #1
+
+declare void @_Z21rsSetElementAt_uchar413rs_allocationDv4_hjj([1 x i32], <4 x i8>, i32, i32) #1
+
+; Function Attrs: nounwind
+define void @.rs.dtor() #0 {
+entry:
+ tail call void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation* nonnull @r) #2
+ tail call void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation* nonnull @w) #2
+ ret void
+}
+
+declare void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation*) #1
+
+attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_var = !{!6, !7, !8}
+!\23rs_object_slots = !{!9, !10}
+!\23rs_export_foreach_name = !{!11, !12}
+!\23rs_export_foreach = !{!13, !14}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"c1", !"7"}
+!7 = !{!"r", !"20"}
+!8 = !{!"w", !"20"}
+!9 = !{!"1"}
+!10 = !{!"2"}
+!11 = !{!"root"}
+!12 = !{!"k1"}
+!13 = !{!"0"}
+!14 = !{!"59"}
+!15 = !{!16, !16, i64 0}
+!16 = !{!"omnipotent char", !17, i64 0}
+!17 = !{!"Simple C/C++ TBAA"}
diff --git a/rsov/compiler/tests/rs_allocation/read_write.rs b/rsov/compiler/tests/rs_allocation/read_write.rs
new file mode 100644
index 0000000..8852c82
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/read_write.rs
@@ -0,0 +1,27 @@
+// Copyright (C) 2016 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+uchar c1;
+rs_allocation r;
+rs_allocation w;
+
+uchar4 __attribute__((kernel)) k1(uchar4 in, uint32_t x, uint32_t y) {
+ uchar4 read = rsGetElementAt_uchar4(r, x, y);
+ rsSetElementAt_uchar4(w, in + c1 + read, x, y);
+ return in;
+}
diff --git a/rsov/compiler/tests/rs_allocation/write.ll b/rsov/compiler/tests/rs_allocation/write.ll
new file mode 100644
index 0000000..0315c45
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/write.ll
@@ -0,0 +1,75 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+; CHECK: %rs_linker_struct___GPUBuffer = OpTypeStruct %uchar
+; CHECK: OpTypePointer Uniform %rs_linker_struct___GPUBuffer
+
+; CHECK: %rs_linker___GPUBlock = OpVariable %{{.+}} Uniform
+
+; CHECK: %main = OpFunction
+
+; CHECK-NOT: OpFunctionCall
+
+; CHECK: OpImageRead %v4uchar
+; CHECK: OpImageWrite
+; CHECK: OpImageWrite
+
+%struct.rs_allocation = type { i32* }
+
+@alloc = common global %struct.rs_allocation zeroinitializer, align 4
+@c1 = common global i8 0, align 1
+
+; Function Attrs: nounwind
+define <4 x i8> @k1(<4 x i8> %in, i32 %x, i32 %y) #0 {
+entry:
+ %0 = load i8, i8* @c1, align 1, !tbaa !13
+ %splat.splatinsert = insertelement <4 x i8> undef, i8 %0, i32 0
+ %splat.splat = shufflevector <4 x i8> %splat.splatinsert, <4 x i8> undef, <4 x i32> zeroinitializer
+ %add = add <4 x i8> %splat.splat, %in
+ %.unpack = load i32, i32* bitcast (%struct.rs_allocation* @alloc to i32*), align 4
+ %1 = insertvalue [1 x i32] undef, i32 %.unpack, 0
+ tail call void @_Z21rsSetElementAt_uchar413rs_allocationDv4_hjj([1 x i32] %1, <4 x i8> %add, i32 %x, i32 %y) #2
+ ret <4 x i8> %in
+}
+
+declare void @_Z21rsSetElementAt_uchar413rs_allocationDv4_hjj([1 x i32], <4 x i8>, i32, i32) #1
+
+; Function Attrs: nounwind
+define void @.rs.dtor() #0 {
+entry:
+ tail call void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation* nonnull @alloc) #2
+ ret void
+}
+
+declare void @_Z13rsClearObjectP13rs_allocation(%struct.rs_allocation*) #1
+
+attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "target-features"="+long64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4, !5}
+!\23rs_export_var = !{!6, !7}
+!\23rs_object_slots = !{!8}
+!\23rs_export_foreach_name = !{!9, !10}
+!\23rs_export_foreach = !{!11, !12}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!"rs_fp_relaxed", !""}
+!6 = !{!"c1", !"7"}
+!7 = !{!"alloc", !"20"}
+!8 = !{!"1"}
+!9 = !{!"root"}
+!10 = !{!"k1"}
+!11 = !{!"0"}
+!12 = !{!"59"}
+!13 = !{!14, !14, i64 0}
+!14 = !{!"omnipotent char", !15, i64 0}
+!15 = !{!"Simple C/C++ TBAA"}
diff --git a/rsov/compiler/tests/rs_allocation/write.rs b/rsov/compiler/tests/rs_allocation/write.rs
new file mode 100644
index 0000000..ef87c7a
--- /dev/null
+++ b/rsov/compiler/tests/rs_allocation/write.rs
@@ -0,0 +1,25 @@
+// Copyright (C) 2016 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(rs2spirv)
+#pragma rs_fp_relaxed
+
+uchar c1;
+rs_allocation alloc;
+
+uchar4 __attribute__((kernel)) k1(uchar4 in, uint32_t x, uint32_t y) {
+ rsSetElementAt_uchar4(alloc, in + c1, x, y);
+ return in;
+}
diff --git a/rsov/compiler/tests/run-lit-tests.sh b/rsov/compiler/tests/run-lit-tests.sh
new file mode 100755
index 0000000..bafbb45
--- /dev/null
+++ b/rsov/compiler/tests/run-lit-tests.sh
@@ -0,0 +1,7 @@
+#!/bin/bash -e
+
+current_dir=$(pwd)
+LIT_PATH=$current_dir/llvm-lit
+LIBSPIRV_TESTS=$current_dir
+
+$LIT_PATH $LIBSPIRV_TESTS $@
diff --git a/rsov/compiler/tests/single_kernel/duff.ll b/rsov/compiler/tests/single_kernel/duff.ll
new file mode 100644
index 0000000..3bb6a53
--- /dev/null
+++ b/rsov/compiler/tests/single_kernel/duff.ll
@@ -0,0 +1,100 @@
+; RUN: llvm-as < %s | rs2spirv -spirv-text -o %t
+; RUN: FileCheck < %t %s
+
+; TODO: Complete the test.
+
+target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-none-linux-gnueabi"
+
+; Function Attrs: norecurse nounwind readnone
+; CHECK: Name [[FooIdx:[0-9]+]] "duff"
+define i32 @duff(i32 %count) #0 {
+entry:
+ %add = add nsw i32 %count, 7
+ %div = sdiv i32 %add, 8
+ %rem = srem i32 %count, 8
+ switch i32 %rem, label %sw.epilog [
+ i32 0, label %do.body
+ i32 7, label %sw.bb1
+ i32 6, label %sw.bb2
+ i32 5, label %sw.bb3
+ i32 4, label %sw.bb6
+ i32 3, label %sw.bb8
+ i32 2, label %sw.bb12
+ i32 1, label %sw.bb14
+ ]
+
+do.body: ; preds = %entry, %sw.bb14
+ %n.0 = phi i32 [ %dec15, %sw.bb14 ], [ %div, %entry ]
+ %x.0 = phi i32 [ %shl, %sw.bb14 ], [ 321, %entry ]
+ %mul = mul nsw i32 %x.0, 5
+ br label %sw.bb1
+
+sw.bb1: ; preds = %entry, %do.body
+ %n.1 = phi i32 [ %n.0, %do.body ], [ %div, %entry ]
+ %x.1 = phi i32 [ %mul, %do.body ], [ 321, %entry ]
+ %dec = add nsw i32 %x.1, -1
+ br label %sw.bb2
+
+sw.bb2: ; preds = %entry, %sw.bb1
+ %n.2 = phi i32 [ %n.1, %sw.bb1 ], [ %div, %entry ]
+ %x.2 = phi i32 [ %dec, %sw.bb1 ], [ 321, %entry ]
+ %xor = xor i32 %x.2, 27
+ br label %sw.bb3
+
+sw.bb3: ; preds = %entry, %sw.bb2
+ %n.3 = phi i32 [ %n.2, %sw.bb2 ], [ %div, %entry ]
+ %x.3 = phi i32 [ %xor, %sw.bb2 ], [ 321, %entry ]
+ %mul4 = mul nsw i32 %x.3, %x.3
+ %sub.neg = add i32 %x.3, 12
+ %sub5 = sub i32 %sub.neg, %mul4
+ br label %sw.bb6
+
+sw.bb6: ; preds = %entry, %sw.bb3
+ %n.4 = phi i32 [ %n.3, %sw.bb3 ], [ %div, %entry ]
+ %x.4 = phi i32 [ %sub5, %sw.bb3 ], [ 321, %entry ]
+ %add7 = add nsw i32 %x.4, 2
+ br label %sw.bb8
+
+sw.bb8: ; preds = %entry, %sw.bb6
+ %n.5 = phi i32 [ %n.4, %sw.bb6 ], [ %div, %entry ]
+ %x.5 = phi i32 [ %add7, %sw.bb6 ], [ 321, %entry ]
+ %rem9 = srem i32 %x.5, 32
+ %mul10 = mul nsw i32 %x.5, %x.5
+ %add11 = add nsw i32 %rem9, %mul10
+ br label %sw.bb12
+
+sw.bb12: ; preds = %entry, %sw.bb8
+ %n.6 = phi i32 [ %n.5, %sw.bb8 ], [ %div, %entry ]
+ %x.6 = phi i32 [ %add11, %sw.bb8 ], [ 321, %entry ]
+ %sub13 = add nsw i32 %x.6, -2
+ br label %sw.bb14
+
+sw.bb14: ; preds = %entry, %sw.bb12
+ %n.7 = phi i32 [ %div, %entry ], [ %n.6, %sw.bb12 ]
+ %x.7 = phi i32 [ 321, %entry ], [ %sub13, %sw.bb12 ]
+ %shl = shl i32 %x.7, 3
+ %dec15 = add nsw i32 %n.7, -1
+ %cmp = icmp sgt i32 %n.7, 1
+ br i1 %cmp, label %do.body, label %sw.epilog
+
+sw.epilog: ; preds = %sw.bb14, %entry
+ %x.8 = phi i32 [ 321, %entry ], [ %shl, %sw.bb14 ]
+ ret i32 %x.8
+}
+
+attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.ident = !{!0}
+!\23pragma = !{!1, !2, !3}
+!\23rs_export_foreach_name = !{!4, !5}
+!\23rs_export_foreach = !{!6, !7}
+
+!0 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!1 = !{!"version", !"1"}
+!2 = !{!"java_package_name", !"rs2spirv"}
+!3 = !{!"rs_fp_relaxed", !""}
+!4 = !{!"root"}
+!5 = !{!"duff"}
+!6 = !{!"0"}
+!7 = !{!"35"}
diff --git a/rsov/compiler/tests/single_kernel/fib.ll b/rsov/compiler/tests/single_kernel/fib.ll
new file mode 100644
index 0000000..916cfc0
--- /dev/null
+++ b/rsov/compiler/tests/single_kernel/fib.ll
@@ -0,0 +1,48 @@
+; RUN: llvm-as < %s | rs2spirv -spirv-text -o %t
+; RUN: FileCheck < %t %s
+
+; TODO: Complete the test.
+
+
+target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-none-linux-gnueabi"
+
+; Function Attrs: nounwind readnone
+; CHECK: Name [[FooIdx:[0-9]+]] "fib"
+define i32 @fib(i32 %n) #0 {
+entry:
+ %n.off8 = add i32 %n, -1
+ %0 = icmp ult i32 %n.off8, 2
+ br i1 %0, label %return, label %if.end
+
+if.end: ; preds = %entry, %if.end
+ %n.tr10 = phi i32 [ %sub2, %if.end ], [ %n, %entry ]
+ %accumulator.tr9 = phi i32 [ %add, %if.end ], [ 1, %entry ]
+ %sub = add nsw i32 %n.tr10, -1
+ %call = tail call i32 @fib(i32 %sub)
+ %sub2 = add nsw i32 %n.tr10, -2
+ %add = add nsw i32 %call, %accumulator.tr9
+ %n.off = add i32 %n.tr10, -3
+ %1 = icmp ult i32 %n.off, 2
+ br i1 %1, label %return, label %if.end
+
+return: ; preds = %if.end, %entry
+ %accumulator.tr.lcssa = phi i32 [ 1, %entry ], [ %add, %if.end ]
+ ret i32 %accumulator.tr.lcssa
+}
+
+attributes #0 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.ident = !{!0}
+!\23pragma = !{!1, !2, !3}
+!\23rs_export_foreach_name = !{!4, !5}
+!\23rs_export_foreach = !{!6, !7}
+
+!0 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!1 = !{!"version", !"1"}
+!2 = !{!"java_package_name", !"rs2spirv"}
+!3 = !{!"rs_fp_relaxed", !""}
+!4 = !{!"root"}
+!5 = !{!"fib"}
+!6 = !{!"0"}
+!7 = !{!"35"}
diff --git a/rsov/compiler/tests/single_kernel/identity.ll b/rsov/compiler/tests/single_kernel/identity.ll
new file mode 100644
index 0000000..a4765a2
--- /dev/null
+++ b/rsov/compiler/tests/single_kernel/identity.ll
@@ -0,0 +1,29 @@
+; RUN: llvm-as < %s | rs2spirv -spirv-text -o %t
+; RUN: FileCheck < %t %s
+target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-none-linux-gnueabi"
+
+; Function Attrs: norecurse nounwind readnone
+; CHECK: Name [[FooIdx:[0-9]+]] "foo"
+; CHECK: Name [[InIdx:[0-9]+]] "in"
+; CHECK: Decorate {{.*}}[[FooIdx]] LinkageAttributes "foo" Export
+define i32 @foo(i32 %in) #0 {
+; CHECK: ReturnValue {{.*}}[[InIdx]]
+ ret i32 %in
+}
+
+attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.ident = !{!0}
+!\23pragma = !{!1, !2, !3}
+!\23rs_export_foreach_name = !{!4, !5}
+!\23rs_export_foreach = !{!6, !7}
+
+!0 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!1 = !{!"version", !"1"}
+!2 = !{!"java_package_name", !"rs2spirv"}
+!3 = !{!"rs_fp_relaxed", !""}
+!4 = !{!"root"}
+!5 = !{!"foo"}
+!6 = !{!"0"}
+!7 = !{!"35"}
diff --git a/rsov/compiler/tests/single_kernel/identity.rs b/rsov/compiler/tests/single_kernel/identity.rs
new file mode 100644
index 0000000..a3bdb6e
--- /dev/null
+++ b/rsov/compiler/tests/single_kernel/identity.rs
@@ -0,0 +1,9 @@
+// TODO: Complete the test.
+
+#pragma version(1)
+#pragma rs java_package_name(rs2srpiv)
+#pragma rs_fp_relaxed
+
+int __attribute__((kernel)) foo(int in) {
+ return in;
+}
diff --git a/rsov/compiler/tests/single_kernel/invert.ll b/rsov/compiler/tests/single_kernel/invert.ll
new file mode 100644
index 0000000..23c458b
--- /dev/null
+++ b/rsov/compiler/tests/single_kernel/invert.ll
@@ -0,0 +1,42 @@
+; RUN: llvm-as < %s | rs2spirv -spirv-text -o %t
+; RUN: FileCheck < %t %s
+
+; TODO: Complete the test.
+
+target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-none-linux-gnueabi"
+
+; Function Attrs: norecurse nounwind readnone
+; CHECK: Name [[FooIdx:[0-9]+]] "invert"
+define <4 x float> @invert(<4 x float> %in) #0 {
+entry:
+ %0 = extractelement <4 x float> %in, i64 0
+ %sub = fsub float 1.000000e+00, %0
+ %1 = insertelement <4 x float> undef, float %sub, i64 0
+ %2 = extractelement <4 x float> %in, i64 1
+ %sub1 = fsub float 1.000000e+00, %2
+ %3 = insertelement <4 x float> %1, float %sub1, i64 1
+ %4 = extractelement <4 x float> %in, i64 2
+ %sub2 = fsub float 1.000000e+00, %4
+ %5 = insertelement <4 x float> %3, float %sub2, i64 2
+ %6 = extractelement <4 x float> %in, i64 3
+ %sub3 = fsub float 1.000000e+00, %6
+ %7 = insertelement <4 x float> %5, float %sub3, i64 3
+ ret <4 x float> %7
+}
+
+attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.ident = !{!0}
+!\23pragma = !{!1, !2, !3}
+!\23rs_export_foreach_name = !{!4, !5}
+!\23rs_export_foreach = !{!6, !7}
+
+!0 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!1 = !{!"version", !"1"}
+!2 = !{!"java_package_name", !"rs2spirv"}
+!3 = !{!"rs_fp_relaxed", !""}
+!4 = !{!"root"}
+!5 = !{!"invert"}
+!6 = !{!"0"}
+!7 = !{!"35"}
diff --git a/rsov/compiler/tests/single_kernel/kernel.ll b/rsov/compiler/tests/single_kernel/kernel.ll
new file mode 100644
index 0000000..43f817c
--- /dev/null
+++ b/rsov/compiler/tests/single_kernel/kernel.ll
@@ -0,0 +1,27 @@
+; RUN: llvm-as < %s | rs2spirv -spirv-text -o %t
+; RUN: FileCheck < %t %s
+target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-none-linux-gnueabi"
+
+; Function Attrs: norecurse nounwind readnone
+; CHECK: Name [[FooIdx:[0-9]+]] "foo"
+; CHECK: Decorate {{.*}}[[FooIdx]] LinkageAttributes "foo" Export
+define void @foo() #0 {
+ ret void
+}
+
+attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.ident = !{!0}
+!\23pragma = !{!1, !2, !3}
+!\23rs_export_foreach_name = !{!4, !5}
+!\23rs_export_foreach = !{!6, !7}
+
+!0 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!1 = !{!"version", !"1"}
+!2 = !{!"java_package_name", !"rs2srpiv"}
+!3 = !{!"rs_fp_relaxed", !""}
+!4 = !{!"root"}
+!5 = !{!"foo"}
+!6 = !{!"0"}
+!7 = !{!"32"}
diff --git a/rsov/compiler/tests/single_kernel/kernel.rs b/rsov/compiler/tests/single_kernel/kernel.rs
new file mode 100644
index 0000000..2e38a13
--- /dev/null
+++ b/rsov/compiler/tests/single_kernel/kernel.rs
@@ -0,0 +1,6 @@
+#pragma version(1)
+#pragma rs java_package_name(rs2spirv)
+#pragma rs_fp_relaxed
+
+void __attribute__((kernel)) foo() {
+}
diff --git a/rsov/compiler/tests/single_kernel/times.ll b/rsov/compiler/tests/single_kernel/times.ll
new file mode 100644
index 0000000..40ee914
--- /dev/null
+++ b/rsov/compiler/tests/single_kernel/times.ll
@@ -0,0 +1,31 @@
+; RUN: llvm-as < %s | rs2spirv -spirv-text -o %t
+; RUN: FileCheck < %t %s
+
+; TODO: Complete the test.
+
+target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-none-linux-gnueabi"
+
+; Function Attrs: norecurse nounwind readnone
+; CHECK: Name [[FooIdx:[0-9]+]] "times"
+define i32 @times(i32 %x) #0 {
+entry:
+ %mul = shl i32 %x, 1
+ ret i32 %mul
+}
+
+attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.ident = !{!0}
+!\23pragma = !{!1, !2, !3}
+!\23rs_export_foreach_name = !{!4, !5}
+!\23rs_export_foreach = !{!6, !7}
+
+!0 = !{!"Android clang version 3.8.256229 (based on LLVM 3.8.256229)"}
+!1 = !{!"version", !"1"}
+!2 = !{!"java_package_name", !"rs2spirv"}
+!3 = !{!"rs_fp_relaxed", !""}
+!4 = !{!"root"}
+!5 = !{!"times"}
+!6 = !{!"0"}
+!7 = !{!"42"}
diff --git a/rsov/compiler/unit_tests/LinkerModuleTests.cpp b/rsov/compiler/unit_tests/LinkerModuleTests.cpp
new file mode 100644
index 0000000..ef4ab80
--- /dev/null
+++ b/rsov/compiler/unit_tests/LinkerModuleTests.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2016, 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 "unit_tests/TestRunner.h"
+
+#include "LinkerModule.h"
+
+using namespace llvm;
+using namespace rs2spirv;
+
+TEST_CASE("SPIRVLine::hasCode_negative") {
+ SPIRVLine L("");
+ CHECK(!L.hasCode());
+
+ L.str() = ";";
+ CHECK(!L.hasCode());
+
+ L.str() = " ;";
+ CHECK(!L.hasCode());
+
+ L.str() = "; OpReturn";
+ CHECK(!L.hasCode());
+
+ L.str() = " ";
+ CHECK(!L.hasCode());
+}
+
+TEST_CASE("SPIRVLine::hasCode_positive") {
+ SPIRVLine L("OpReturn");
+ CHECK(L.hasCode());
+
+ L.str() = " OpReturn ";
+ CHECK(L.hasCode());
+
+ L.str() = "OpReturn;";
+ CHECK(L.hasCode());
+
+ L.str() = "OpReturn ;";
+ CHECK(L.hasCode());
+}
+
+TEST_CASE("SPIRVLine::getIdentifiers") {
+ using Vector = SmallVector<StringRef, 4>;
+ Vector Ids;
+
+ SPIRVLine L("OpReturn");
+ L.getIdentifiers(Ids);
+ CHECK((Ids == Vector{}));
+ Ids.clear();
+
+ L.str() = "%uint = OpTypeInt 32 0";
+ L.getIdentifiers(Ids);
+ CHECK((Ids == Vector{"%uint"}));
+ Ids.clear();
+
+ L.str() = "%x = OpTypeStruct %float";
+ L.getIdentifiers(Ids);
+ CHECK((Ids == Vector{"%x", "%float"}));
+ Ids.clear();
+}
+
+TEST_CASE("SPIRVLine::getLHSIdentifier") {
+ SPIRVLine L("OpReturn");
+ CHECK(!L.getLHSIdentifier());
+
+ L.str() = "%uint = OpTypeInt 32 0";
+ auto Id = L.getLHSIdentifier();
+ CHECK(Id);
+ CHECK(*Id == "%uint");
+
+ L.str() = "%12 = OpConstant %uint 0";
+ Id = L.getLHSIdentifier();
+ CHECK(Id);
+ CHECK(*Id == "%12");
+}
+
+TEST_CASE("SPIRVLine::getRHSIdentifiers") {
+ using Vector = SmallVector<StringRef, 4>;
+ Vector Ids;
+
+ SPIRVLine L("OpReturn");
+ L.getRHSIdentifiers(Ids);
+ CHECK((Ids == Vector{}));
+ Ids.clear();
+
+ L.str() = "%uint = OpTypeInt 32 0";
+ L.getRHSIdentifiers(Ids);
+ CHECK((Ids == Vector{}));
+ Ids.clear();
+
+ L.str() = "%x = OpTypeStruct %float";
+ L.getRHSIdentifiers(Ids);
+ CHECK((Ids == Vector{"%float"}));
+ Ids.clear();
+
+ L.str() = "%x = OpTypeStruct %float %uint";
+ L.getRHSIdentifiers(Ids);
+ CHECK((Ids == Vector{"%float", "%uint"}));
+ Ids.clear();
+}
+
+TEST_CASE("SPIRVLine::getRHS") {
+ SPIRVLine L("OpReturn");
+ auto Res = L.getRHS();
+ CHECK(!Res);
+
+ L.str() = "%float = OpTypeFloat 32";
+ Res = L.getRHS();
+ CHECK(Res);
+ CHECK(*Res == "OpTypeFloat 32");
+}
+
+TEST_CASE("SPIRVLine::replaceId") {
+ SPIRVLine L("OpReturn");
+ bool Res = L.replaceId("%uint", "%void");
+ CHECK(!Res);
+ CHECK(L.str() == "OpReturn");
+
+ L.str() = "%entry = OpLabel";
+ Res = L.replaceId("%wtw", "%twt");
+ CHECK(!Res);
+ CHECK(L.str() == "%entry = OpLabel");
+
+ Res = L.replaceId("%entry", "%x");
+ CHECK(Res);
+ CHECK(L.str() == "%x = OpLabel");
+
+ L.str() = "%7 = OpTypeFunction %v4float %v4float";
+ Res = L.replaceId("%7", "%8");
+ CHECK(Res);
+ CHECK(L.str() == "%8 = OpTypeFunction %v4float %v4float");
+ Res = L.replaceId("%v4float", "%void");
+ CHECK(Res);
+ CHECK(L.str() == "%8 = OpTypeFunction %void %v4float");
+ Res = L.replaceId("%v4float", "%void");
+ CHECK(Res);
+ CHECK(L.str() == "%8 = OpTypeFunction %void %void");
+}
+
+TEST_CASE("SPIRVLine::replaceStr") {
+ SPIRVLine L("OpReturn");
+ bool Res = L.replaceStr("OpLoad", "OpStore");
+ CHECK(!Res);
+ CHECK(L.str() == "OpReturn");
+ Res = L.replaceStr("OpReturn", "OpFunctionEnd");
+ CHECK(Res);
+ CHECK(L.str() == "OpFunctionEnd");
+
+ L.str() = "%16 = OpUndef %v4float";
+ Res = L.replaceStr("OpUndef", "OpDef");
+ CHECK(Res);
+ CHECK(L.str() == "%16 = OpDef %v4float");
+}
diff --git a/rsov/compiler/unit_tests/TestRunner.h b/rsov/compiler/unit_tests/TestRunner.h
new file mode 100644
index 0000000..4e8f57a
--- /dev/null
+++ b/rsov/compiler/unit_tests/TestRunner.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2016, 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_TEST_RUNNER
+#define RS2SPIRV_TEST_RUNNER
+
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <cassert>
+#include <vector>
+
+namespace rs2spirv {
+
+struct TestCase {
+ void (*testPtr)(void);
+ const char *const description;
+};
+
+class TestRunnerContext {
+public:
+ static TestRunnerContext &getInstance() {
+ static TestRunnerContext ctx;
+ return ctx;
+ }
+
+ static void addTest(TestCase TC) { getInstance().tests.push_back(TC); }
+ static size_t &getCheckSuccessNum() { return getInstance().checkSuccessNum; }
+ static size_t &getTotalCheckNum() { return getInstance().totalCheckNum; }
+
+ static int runTests() {
+ bool Failed = false;
+ for (auto &TC : getInstance().tests) {
+ getCheckSuccessNum() = getTotalCheckNum() = 0;
+ llvm::outs() << "Test(" << TC.description << ") {\n";
+ TC.testPtr();
+ llvm::outs() << "\n} (" << TC.description << ") [" << getCheckSuccessNum()
+ << "/" << getTotalCheckNum() << "]\n\n";
+ Failed |= getCheckSuccessNum() != getTotalCheckNum();
+ }
+
+ return static_cast<int>(Failed);
+ }
+
+private:
+ TestRunnerContext() = default;
+ std::vector<TestCase> tests;
+ size_t checkSuccessNum;
+ size_t totalCheckNum;
+};
+
+struct TestAdder {
+ TestAdder(TestCase TC) { TestRunnerContext::addTest(TC); }
+};
+
+#define RS2SPIRV_CONCAT_IMPL(S1, S2) S1##S2
+#define RS2SPIRV_CONCAT(S1, S2) RS2SPIRV_CONCAT_IMPL(S1, S2)
+#define RS2SPIRV_ANONYMOUS(X) RS2SPIRV_CONCAT(X, __COUNTER__)
+
+#if RS2SPIRV_DEBUG
+#define RS2SPIRV_TEST_CASE_ADD_IMPL(FNAME, VNAME, DESCRIPTION) \
+ static void FNAME(); \
+ static rs2spirv::TestAdder VNAME({FNAME, DESCRIPTION}); \
+ inline void FNAME()
+#elif defined(__GNUC__) || defined(__clang__)
+#define RS2SPIRV_TEST_CASE_ADD_IMPL(FNAME, VNAME, DESCRIPTION) \
+ static inline void __attribute__((unused)) FNAME()
+#else
+#define RS2SPIRV_TEST_CASE_ADD_IMPL(FNAME, VNAME, DESCRIPTION) \
+ static inline void FNAME()
+#endif
+
+#define RS2SPIRV_TEST_CASE_ADD(NAME, DESCRIPTION) \
+ RS2SPIRV_TEST_CASE_ADD_IMPL(RS2SPIRV_ANONYMOUS(NAME), \
+ RS2SPIRV_ANONYMOUS(NAME), DESCRIPTION)
+
+#define TEST_CASE(DESCRIPTION) RS2SPIRV_TEST_CASE_ADD(TC, DESCRIPTION)
+
+#define CHECK(CONDITION) \
+ ++rs2spirv::TestRunnerContext::getTotalCheckNum(); \
+ if (!(CONDITION)) \
+ llvm::errs() << "\nCHECK <( " #CONDITION " )> failed!\n"; \
+ else \
+ ++rs2spirv::TestRunnerContext::getCheckSuccessNum(); \
+ (void)0
+
+} // namespace rs2spirv
+
+#endif