SubzeroReactor: add stubbed coroutine support

* Implement Nucleus::createCoroutine and acquireCoroutine so that they
create a regular function for the Begin entry, and stubbed functions
for the Await and Destroy entries.
* Made Routine::getEntry const as Coroutine expects this function to
be callable from multiple threads. Subzero's implementation would
lazily finalize the entry and cache it. Now we finalize it eagerly in
acquireRoutine, and getEntry simply returns pointers.

Bug: b/130459196
Change-Id: Id9fa447ce55c7df6fb55e1c878ca021e825fecfa
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/38873
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Tested-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index 0653574..003716e 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -405,7 +405,7 @@
 			}
 		}
 
-		const void *getEntry(int index) override
+		const void *getEntry(int index) const override
 		{
 			return addresses[index];
 		}
diff --git a/src/Reactor/Routine.hpp b/src/Reactor/Routine.hpp
index f5dad2c..4e643fd 100644
--- a/src/Reactor/Routine.hpp
+++ b/src/Reactor/Routine.hpp
@@ -25,7 +25,7 @@
 		Routine() = default;
 		virtual ~Routine() = default;
 
-		virtual const void *getEntry(int index = 0) = 0;
+		virtual const void *getEntry(int index = 0) const = 0;
 	};
 
 	// RoutineT is a type-safe wrapper around a Routine and its callable entry, returned by FunctionT
diff --git a/src/Reactor/SubzeroReactor.cpp b/src/Reactor/SubzeroReactor.cpp
index 78d0b9a..60daccb 100644
--- a/src/Reactor/SubzeroReactor.cpp
+++ b/src/Reactor/SubzeroReactor.cpp
@@ -490,7 +490,7 @@
 		ELFMemoryStreamer &operator=(const ELFMemoryStreamer &) = delete;
 
 	public:
-		ELFMemoryStreamer() : Routine(), entry(nullptr)
+		ELFMemoryStreamer() : Routine()
 		{
 			position = 0;
 			buffer.reserve(0x1000);
@@ -534,28 +534,35 @@
 
 		void seek(uint64_t Off) override { position = Off; }
 
-		const void *getEntry(int index) override
+		const void* finalizeEntryBegin()
 		{
-			ASSERT(index == 0); // Subzero does not support multiple entry points per routine yet.
-			if(!entry)
-			{
-				position = std::numeric_limits<std::size_t>::max();   // Can't stream more data after this
+			position = std::numeric_limits<std::size_t>::max();   // Can't stream more data after this
 
-				size_t codeSize = 0;
-				entry = loadImage(&buffer[0], codeSize);
+			size_t codeSize = 0;
+			const void *entry = loadImage(&buffer[0], codeSize);
 
-				#if defined(_WIN32)
-					VirtualProtect(&buffer[0], buffer.size(), PAGE_EXECUTE_READ, &oldProtection);
-					FlushInstructionCache(GetCurrentProcess(), NULL, 0);
-				#else
-					mprotect(&buffer[0], buffer.size(), PROT_READ | PROT_EXEC);
-					__builtin___clear_cache((char*)entry, (char*)entry + codeSize);
-				#endif
-			}
-
+#if defined(_WIN32)
+			VirtualProtect(&buffer[0], buffer.size(), PAGE_EXECUTE_READ, &oldProtection);
+			FlushInstructionCache(GetCurrentProcess(), NULL, 0);
+#else
+			mprotect(&buffer[0], buffer.size(), PROT_READ | PROT_EXEC);
+			__builtin___clear_cache((char*)entry, (char*)entry + codeSize);
+#endif
 			return entry;
 		}
 
+		void setEntry(int index, const void* func)
+		{
+			ASSERT(func);
+			funcs[index] = func;
+		}
+
+		const void *getEntry(int index) const override
+		{
+			ASSERT(funcs[index]);
+			return funcs[index];
+		}
+
 		const void* addConstantData(const void* data, size_t size)
 		{
 			auto buf = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
@@ -566,7 +573,7 @@
 		}
 
 	private:
-		void *entry;
+		std::array<const void*, Nucleus::CoroutineEntryCount> funcs = {};
 		std::vector<uint8_t, ExecutableAllocator<uint8_t>> buffer;
 		std::size_t position;
 		std::vector<std::unique_ptr<uint8_t[]>> constantData;
@@ -704,6 +711,9 @@
 		objectWriter->setUndefinedSyms(::context->getConstantExternSyms());
 		objectWriter->writeNonUserSections();
 
+		const void* entryBegin = ::routine->finalizeEntryBegin();
+		::routine->setEntry(Nucleus::CoroutineEntryBegin, entryBegin);
+
 		Routine *handoffRoutine = ::routine;
 		::routine = nullptr;
 
@@ -3609,8 +3619,28 @@
 	void EmitDebugVariable(Value* value) {}
 	void FlushDebug() {}
 
-	void Nucleus::createCoroutine(Type *YieldType, std::vector<Type*> &Params) { UNIMPLEMENTED("createCoroutine"); }
-	std::shared_ptr<Routine> Nucleus::acquireCoroutine(const char *name, const Config::Edit &cfgEdit /* = Config::Edit::None */) { UNIMPLEMENTED("acquireCoroutine"); return nullptr; }
+	void Nucleus::createCoroutine(Type *YieldType, std::vector<Type*> &Params)
+	{
+		// Subzero currently only supports coroutines as functions (i.e. that do not yield)
+		createFunction(YieldType, Params);
+	}
+
+	static bool coroutineEntryAwaitStub(Nucleus::CoroutineHandle, void* yieldValue) { return false; }
+	static void coroutineEntryDestroyStub(Nucleus::CoroutineHandle) {}
+
+	std::shared_ptr<Routine> Nucleus::acquireCoroutine(const char *name, const Config::Edit &cfgEdit /* = Config::Edit::None */)
+	{
+		// acquireRoutine sets the CoroutineEntryBegin entry
+		auto coroutineEntry = acquireRoutine(name, cfgEdit);
+
+		// For now, set the await and destroy entries to stubs, until we add proper coroutine support to the Subzero backend
+		auto routine = std::static_pointer_cast<ELFMemoryStreamer>(coroutineEntry);
+		routine->setEntry(Nucleus::CoroutineEntryAwait, reinterpret_cast<const void*>(&coroutineEntryAwaitStub));
+		routine->setEntry(Nucleus::CoroutineEntryDestroy, reinterpret_cast<const void*>(&coroutineEntryDestroyStub));
+
+		return coroutineEntry;
+	}
+
 	void Nucleus::yield(Value* val) { UNIMPLEMENTED("Yield"); }
 
 }