text: #include "WAVM/Runtime/Intrinsics.h"
  #include <initializer_list>
  #include <string>
  #include <utility>
  #include <vector>
  #include "RuntimePrivate.h"
  #include "WAVM/IR/FeatureSpec.h"
  #include "WAVM/IR/Module.h"
  #include "WAVM/IR/Operators.h"
  #include "WAVM/IR/Types.h"
  #include "WAVM/IR/Validate.h"
  #include "WAVM/Inline/Errors.h"
  #include "WAVM/Inline/Hash.h"
  #include "WAVM/Inline/HashMap.h"
  #include "WAVM/Inline/Serialization.h"
  #include "WAVM/Inline/Timing.h"
  #include "WAVM/Platform/RWMutex.h"
  #include "WAVM/Runtime/Runtime.h"
  
  #ifdef WAVM_HAS_TRACY
  #include <Tracy.hpp>
  #endif
  
  namespace WAVM { namespace Intrinsics {
  	struct ModuleImpl
  	{
  		HashMap<std::string, Intrinsics::Function*> functionMap;
  		HashMap<std::string, Intrinsics::Table*> tableMap;
  		HashMap<std::string, Intrinsics::Memory*> memoryMap;
  		HashMap<std::string, Intrinsics::Global*> globalMap;
  	};
  }}
  
  using namespace WAVM;
  using namespace WAVM::Runtime;
  using namespace WAVM::IR;
  
  Intrinsics::Module::~Module()
  {
  	if(impl) { delete impl; }
  }
  
  static void initializeModule(Intrinsics::Module* moduleRef)
  {
  	if(!moduleRef->impl) { moduleRef->impl = new Intrinsics::ModuleImpl; }
  }
  
  Intrinsics::Function::Function(Intrinsics::Module* moduleRef,
  							   const char* inName,
  							   void* inNativeFunction,
  							   FunctionType inType)
  : name(inName), type(inType), nativeFunction(inNativeFunction)
  {
  	initializeModule(moduleRef);
  
  	if(moduleRef->impl->functionMap.contains(name))
  	{
  		Errors::fatalf("Intrinsic function already registered: %s", name);
  	}
  	moduleRef->impl->functionMap.set(name, this);
  }
  
  Intrinsics::Global::Global(Intrinsics::Module* moduleRef,
  						   const char* inName,
  						   ValueType inType,
  						   Value inValue)
  : name(inName), type(inType), value(inValue)
  {
  	initializeModule(moduleRef);
  
  	if(moduleRef->impl->globalMap.contains(name))
  	{
  		Errors::fatalf("Intrinsic global already registered: %s", name);
  	}
  	moduleRef->impl->globalMap.set(name, this);
  }
  
  Intrinsics::Table::Table(Intrinsics::Module* moduleRef, const char* inName, const TableType& inType)
  : name(inName), type(inType)
  {
  	initializeModule(moduleRef);
  
  	if(moduleRef->impl->tableMap.contains(name))
  	{
  		Errors::fatalf("Intrinsic table already registered: %s", name);
  	}
  	moduleRef->impl->tableMap.set(name, this);
  }
  
  Intrinsics::Memory::Memory(Intrinsics::Module* moduleRef,
  						   const char* inName,
  						   const MemoryType& inType)
  : name(inName), type(inType)
  {
  	initializeModule(moduleRef);
  
  	if(moduleRef->impl->memoryMap.contains(name))
  	{
  		Errors::fatalf("Intrinsic memory already registered: %s", name);
  	}
  	moduleRef->impl->memoryMap.set(name, this);
  }
  
  Instance* Intrinsics::instantiateModule(
  	Compartment* compartment,
  	const std::initializer_list<const Intrinsics::Module*>& moduleRefs,
  	std::string&& debugName)
  {
  #ifdef WAVM_HAS_TRACY
  	ZoneScopedN("Intrinsics::instantiateModule");
  #endif
  	Timing::Timer timer;
  
  	IR::Module irModule(FeatureLevel::wavm);
  	irModule.featureSpec.nonWASMFunctionTypes = true;
  	DisassemblyNames names;
  
  	std::vector<FunctionImportBinding> functionImportBindings;
  	for(const Intrinsics::Module* moduleRef : moduleRefs)
  	{
  		if(moduleRef->impl)
  		{
  			size_t reserveSize = moduleRef->impl->functionMap.size();
  			functionImportBindings.reserve(reserveSize);
  			irModule.types.reserve(reserveSize);
  			irModule.types.reserve(reserveSize);
  			irModule.functions.imports.reserve(reserveSize);
  			irModule.imports.reserve(reserveSize);
  			names.functions.reserve(reserveSize);
  			for(const auto& pair : moduleRef->impl->functionMap)
  			{
  				functionImportBindings.push_back({pair.value->getNativeFunction()});
  				const Uptr typeIndex = irModule.types.size();
  				const Uptr functionIndex = irModule.functions.size();
  				irModule.types.push_back(pair.value->getType());
  				irModule.functions.imports.push_back({{typeIndex}, "", pair.value->getName()});
  				irModule.imports.push_back({ExternKind::function, functionIndex});
  				names.functions.push_back({pair.value->getName(), {}, {}});
  			}
  
  			for(const auto& pair : moduleRef->impl->tableMap)
  			{
  				const Uptr tableIndex = irModule.tables.size();
  				irModule.tables.defs.push_back({pair.value->getType()});
  				names.tables.push_back(pair.value->getName());
  				irModule.exports.push_back({pair.value->getName(), ExternKind::table, tableIndex});
  			}
  
  			for(const auto& pair : moduleRef->impl->memoryMap)
  			{
  				const Uptr memoryIndex = irModule.memories.size();
  				irModule.memories.defs.push_back({pair.value->getType()});
  				names.memories.push_back(pair.value->getName());
  				irModule.exports.push_back(
  					{pair.value->getName(), ExternKind::memory, memoryIndex});
  			}
  
  			for(const auto& pair : moduleRef->impl->globalMap)
  			{
  				InitializerExpression initializerExpression;
  				switch(pair.value->getType())
  				{
  				case ValueType::i32:
  					initializerExpression.type = InitializerExpression::Type::i32_const;
  					initializerExpression.i32 = pair.value->getValue().i32;
  					break;
  				case ValueType::i64:
  					initializerExpression.type = InitializerExpression::Type::i64_const;
  					initializerExpression.i64 = pair.value->getValue().i64;
  					break;
  				case ValueType::f32:
  					initializerExpression.type = InitializerExpression::Type::f32_const;
  					initializerExpression.f32 = pair.value->getValue().f32;
  					break;
  				case ValueType::f64:
  					initializerExpression.type = InitializerExpression::Type::f64_const;
  					initializerExpression.f64 = pair.value->getValue().f64;
  					break;
  				case ValueType::v128:
  					initializerExpression.type = InitializerExpression::Type::v128_const;
  					initializerExpression.v128 = pair.value->getValue().v128;
  					break;
  
  				case ValueType::externref:
  				case ValueType::funcref:
  					Errors::fatal("Intrinsic reference-typed globals are not supported");
  
  				case ValueType::none:
  				case ValueType::any:
  				default: WAVM_UNREACHABLE();
  				};
  
  				const Uptr globalIndex = irModule.globals.size();
  				irModule.globals.defs.push_back(
  					{GlobalType(pair.value->getType(), false), initializerExpression});
  				names.globals.push_back(pair.value->getName());
  				irModule.exports.push_back(
  					{pair.value->getName(), ExternKind::global, globalIndex});
  			}
  		}
  	}
  
  	{
  		size_t reserveSize = irModule.functions.imports.size();
  		irModule.types.reserve(reserveSize);
  		irModule.functions.defs.reserve(reserveSize);
  		names.functions.reserve(reserveSize);
  		irModule.exports.reserve(reserveSize);
  	}
  	// Generate thunks for the intrinsic functions.
  	for(Uptr functionImportIndex = 0; functionImportIndex < irModule.functions.imports.size();
  		++functionImportIndex)
  	{
  		const FunctionImport& functionImport = irModule.functions.imports[functionImportIndex];
  		const FunctionType intrinsicFunctionType = irModule.types[functionImport.type.index];
  		const FunctionType wasmFunctionType(intrinsicFunctionType.results(),
  											intrinsicFunctionType.params(),
  											CallingConvention::wasm);
  
  		const Uptr wasmFunctionTypeIndex = irModule.types.size();
  		irModule.types.push_back(wasmFunctionType);
  
  		Serialization::ArrayOutputStream codeStream;
  		OperatorEncoderStream opEncoder(codeStream);
  		for(Uptr paramIndex = 0; paramIndex < intrinsicFunctionType.params().size(); ++paramIndex)
  		{
  			opEncoder.local_get({paramIndex});
  		}
  		opEncoder.call({functionImportIndex});
  		opEncoder.end();
  
  		const Uptr wasmFunctionIndex = irModule.functions.size();
  		irModule.functions.defs.push_back({{wasmFunctionTypeIndex}, {}, codeStream.getBytes(), {}});
  		names.functions.push_back({"thunk:" + functionImport.exportName, {}, {}});
  		irModule.exports.push_back(
  			{functionImport.exportName, ExternKind::function, wasmFunctionIndex});
  	}
  
  	{
  		setDisassemblyNames(irModule, names);
  	}
  
  	if(WAVM_ENABLE_ASSERTS)
  	{
  		try
  		{
  			std::shared_ptr<IR::ModuleValidationState> moduleValidationState
  				= IR::createModuleValidationState(irModule);
  			validatePreCodeSections(*moduleValidationState);
  			validateCodeSection(*moduleValidationState);
  			validatePostCodeSections(*moduleValidationState);
  		}
  		catch(ValidationException const& exception)
  		{
  			Errors::fatalf("Validation exception in intrinsic module: %s",
  						   exception.message.c_str());
  		}
  	}
  
  	ModuleRef module;
  	{
  #ifdef WAVM_HAS_TRACY
  		ZoneNamedN(_zc, "compileModule", true);
  #endif
  		module = compileModule(irModule);
  	}
  	Instance* instance = instantiateModuleInternal(compartment,
  												   module,
  												   std::move(functionImportBindings),
  												   {},
  												   {},
  												   {},
  												   {},
  												   std::move(debugName));
  
  	Timing::logTimer("Instantiated intrinsic module", timer);
  	return instance;
  }
  
  HashMap<std::string, Intrinsics::Function*> Intrinsics::getUninstantiatedFunctions(
  	const std::initializer_list<const Intrinsics::Module*>& moduleRefs)
  {
  	HashMap<std::string, Intrinsics::Function*> result;
  
  	for(const Intrinsics::Module* moduleRef : moduleRefs)
  	{
  		if(moduleRef->impl)
  		{
  			for(const auto& pair : moduleRef->impl->functionMap)
  			{
  				result.addOrFail(pair.key, pair.value);
  			}
  		}
  	}
  
  	return result;
  }
  