#include "vm.h" // Implementation of the Slang bytecode VM // #include "bytecode.h" #include "ir.h" #include "../../slang.h" namespace Slang { struct VMValImpl { // opcode used to construct the value uint32_t op; }; // Representation of a type during VM execution struct VMTypeImpl : VMValImpl { // number of arguments to the type uint32_t argCount; // Size and alignment of instances of this // type. UInt size; UInt alignment; // operands follow }; struct VMVal { VMValImpl* impl; VMValImpl* getImpl() { return impl; } }; struct VMType : VMVal { VMTypeImpl* getImpl() { return (VMTypeImpl*) impl; } UInt getSize() { return getImpl()->size; } UInt getAlignment() { return getImpl()->alignment; } }; struct VMPtrTypeImpl : VMTypeImpl { VMType base; }; struct VMReg { // Type that the register is meant to hold VMType type; // offset of the variable inside the frame size_t offset; }; struct VMConst { // Type of the constant VMType type; // Operand address to use void* ptr; }; struct VMModule; // Information about a function after it has been // loaded into the VM. struct VMFunc { // The parent module that this function belongs to VMModule* module; BCFunc* bcFunc; VMReg* regs; VMConst* consts; size_t frameSize; }; struct VMFrame { // The function from which this frame was spawned VMFunc* func; // The parent frame on the call stack of the // current thread. VMFrame* parent; // The instruction pointer within this frame BCOp* ip; // Registers are stored after this point. }; struct VM; struct VMModule { BCModule* bcModule; VM* vm; void** symbols; VMType* types; }; UInt decodeUInt(BCOp** ioPtr) { BCOp* ptr = *ioPtr; UInt value = *ptr++; if( value < 128 ) { *ioPtr = ptr; return value; } // Slower path for variable-length encoding UInt result = 0; for(;;) { value = value & 0x7F; result = (result << 7) | value; if(value < 127) { *ioPtr = ptr; return value; } value = *ptr++; } } Int decodeSInt(BCOp** ioPtr) { UInt uVal = decodeUInt(ioPtr); if( uVal & 1 ) { return Int(~(uVal >> 1)); } else { return Int(uVal >> 1); } } void* getRegPtrImpl(VMFrame* frame, UInt id) { VMFunc* vmFunc = frame->func; VMReg* vmReg = &vmFunc->regs[id]; size_t offset = vmReg->offset; return (void*)((char*)frame + offset); } void* getOperandPtrImpl(VMFrame* frame, Int id) { if( id >= 0 ) { // This ID represents a local variable/register // of the current call frame, and should // be used to index into a table of such values. return getRegPtrImpl(frame, id); } else { // This ID represents a global value that has // been imported into the current func, and // should be looked up via indirection into // the current module. VMFunc* vmFunc = frame->func; VMConst* vmConst = &vmFunc->consts[~id]; return vmConst->ptr; } } VMType getOperandTypeImpl(VMFrame* frame, Int id) { if( id >= 0 ) { return frame->func->regs[id].type; } else { return frame->func->consts[~id].type; } } struct VMPtrAndType { void* ptr; VMType type; }; VMPtrAndType decodeOperandPtrAndType(VMFrame* frame, BCOp** ioIP) { Int id = decodeSInt(ioIP); VMPtrAndType ptrAndType; ptrAndType.ptr = getOperandPtrImpl(frame, id); ptrAndType.type = getOperandTypeImpl(frame, id); return ptrAndType; } void* decodeOperandPtrImpl(VMFrame* frame, BCOp** ioIP) { Int id = decodeSInt(ioIP); return getOperandPtrImpl(frame, id); } template T* decodeOperandPtr(VMFrame* frame, BCOp** ioIP) { return (T*)decodeOperandPtrImpl(frame, ioIP); } template T& decodeOperand(VMFrame* frame, BCOp** ioIP) { return *decodeOperandPtr(frame, ioIP); } VMType decodeType(VMFrame* frame, BCOp** ioIP) { UInt id = decodeUInt(ioIP); return frame->func->module->types[id]; } VMFunc* loadVMFunc( BCFunc* bcFunc, VMModule* vmModule); struct VMSizeAlign { UInt size; UInt align; }; VMSizeAlign getVMSymbolSize(BCSymbol* symbol) { VMSizeAlign result; result.size = sizeof(void*); result.align = sizeof(void*); switch( symbol->op ) { default: SLANG_UNEXPECTED("op"); break; case kIROp_TypeType: break; case kIROp_Func: { BCFunc* func = (BCFunc*) symbol; result.size = sizeof(VMFunc) + func->regCount * sizeof(VMReg) + func->constCount * sizeof(VMConst); } break; } return result; } VMType getType( VMModule* vmModule, uint32_t typeID) { return vmModule->types[typeID]; } void* getGlobalPtr( VMModule* vmModule, uint32_t globalID) { return vmModule->symbols[globalID]; } VMType getGlobalType( VMModule* vmModule, uint32_t globalID) { return getType(vmModule, vmModule->bcModule->symbols[globalID]->typeID); } VMFunc* loadVMFunc( BCFunc* bcFunc, VMModule* vmModule) { UInt regCount = bcFunc->regCount; UInt constCount = bcFunc->constCount; UInt vmFuncSize = sizeof(VMFunc) + regCount * sizeof(VMReg) + constCount * sizeof(VMConst); VMFunc* vmFunc = (VMFunc*) malloc(vmFuncSize); VMReg* vmRegs = (VMReg*) (vmFunc + 1); VMConst* vmConsts = (VMConst*) (vmRegs + regCount); vmFunc->module = vmModule; vmFunc->bcFunc = bcFunc; vmFunc->regs = vmRegs; vmFunc->consts = vmConsts; UInt offset = sizeof(VMFrame); for( UInt rr = 0; rr < regCount; ++rr ) { BCReg* bcReg = &bcFunc->regs[rr]; auto bcTypeID = bcReg->typeID; // We expect the type to come from the outer module, so // that we can allocate space for it as we go. auto vmType = getType(vmModule, bcTypeID); auto regSize = vmType.getSize(); auto regAlign = vmType.getAlignment(); offset = (offset + (regAlign-1)) & ~(regAlign-1); size_t regOffset = offset; offset += regSize; vmRegs[rr].type = vmType; vmRegs[rr].offset = regOffset; } vmFunc->frameSize = offset; for( UInt cc = 0; cc < constCount; ++cc ) { BCConst bcConst = bcFunc->consts[cc]; switch( bcConst.flavor ) { case kBCConstFlavor_GlobalSymbol: { auto globalID = bcConst.id; vmFunc->consts[cc].ptr = &vmModule->symbols[globalID]; vmFunc->consts[cc].type = getGlobalType(vmModule, globalID); } break; case kBCConstFlavor_Constant: { auto constID = bcConst.id; auto constInfo = &vmModule->bcModule->constants[constID]; vmFunc->consts[cc].ptr = constInfo->ptr; #if 0 fprintf(stderr, "CONSANT[%d] : [%p]\n", (int)cc, vmFunc->consts[cc].ptr); fprintf(stderr, "BC [%p] : %d\n", &constInfo->ptr, (int)constInfo->ptr.rawVal); #endif vmFunc->consts[cc].type = getType(vmModule, constInfo->typeID); } break; } } return vmFunc; } VMFrame* createFrame(VMFunc* vmFunc) { VMFrame* vmFrame = (VMFrame*) malloc(vmFunc->frameSize); vmFrame->func = vmFunc; vmFrame->ip = vmFunc->bcFunc->blocks[0].code; return vmFrame; } void dumpVMFrame(VMFrame* vmFrame) { fflush(stderr); // We want to walk the VM frame and dump the // state of all of its logical registers. // For now this is made easier by having // no overlapping register assignments... VMFunc* vmFunc = vmFrame->func; BCFunc* bcFunc = vmFunc->bcFunc; UInt regCount = bcFunc->regCount; for (UInt rr = 0; rr < regCount; ++rr) { VMType regType = getOperandTypeImpl(vmFrame, rr); void* regData = getRegPtrImpl(vmFrame, rr); char const* name = bcFunc->regs[rr].name; // Use the type to print the data... fprintf(stderr, "0x%p: ", regData); fprintf(stderr, "%%%u ", (unsigned int) rr); if (name) { fprintf(stderr, "\"%s\" ", name); } if (regType.impl) { switch (regType.impl->op) { case kIROp_TypeType: // TODO: we could recursively go and print types... fprintf(stderr, ": Type = ???"); break; case kIROp_readWriteStructuredBufferType: fprintf(stderr, ": RWStructuredBuffer = ???"); break; case kIROp_structuredBufferType: fprintf(stderr, ": StructuredBuffer = ???"); break; case kIROp_BoolType: fprintf(stderr, ": Bool = %s", *(bool*)regData ? "true" : "false"); break; case kIROp_Int32Type: fprintf(stderr, ": Int32 = %d", *(int32_t*)regData); break; case kIROp_UInt32Type: fprintf(stderr, ": UInt32 = %u", *(uint32_t*)regData); break; case kIROp_PtrType: { fprintf(stderr, ": Ptr = [%p]", *(void**)regData); } break; default: fprintf(stderr, ""); break; } } else { fprintf(stderr, ": "); } fprintf(stderr, "; // "); fprintf(stderr, "%s", getIROpInfo((IROp) bcFunc->regs[rr].op).name); fprintf(stderr, "\n"); // Okay, now we need to use the type // stored in the VM register to tell // us how to print things. } IROp op = IROp(*vmFrame->ip); IROpInfo opInfo = getIROpInfo(op); fprintf(stderr, "NEXT op: %s\n", opInfo.name); fflush(stderr); } struct VM { }; VM* createVM() { VM* vm = new VM(); return vm; } struct VMThread { // The currently executing call frame VMFrame* frame; }; void resumeThread( VMThread* vmThread); void computeTypeSizeAlign( VMTypeImpl* impl) { UInt size = 0; UInt alignment = 0; switch(impl->op) { case kIROp_VoidType: size = 0; break; case kIROp_BoolType: size = 1; break; case kIROp_Int32Type: case kIROp_UInt32Type: case kIROp_Float32Type: size = 4; break; case kIROp_FuncType: case kIROp_PtrType: case kIROp_readWriteStructuredBufferType: case kIROp_structuredBufferType: size = sizeof(void*); break; default: SLANG_UNIMPLEMENTED_X("type sizing"); UNREACHABLE(impl->size = 0); break; } if(!alignment) alignment = size; if(!alignment) alignment = 1; impl->size = size; impl->alignment = alignment; } VMType getType( VM* /*vm*/, VMTypeImpl* typeImpl) { // TODO: need to look up an existing type that matches... UInt argCount = typeImpl->argCount; UInt size = sizeof(VMTypeImpl) + argCount*sizeof(VMType); VMTypeImpl* impl = (VMTypeImpl*) malloc(size); memcpy(impl, typeImpl, size); computeTypeSizeAlign(impl); VMType type; type.impl = impl; return type; } VMVal getVal( VMModule* vmModule, UInt index) { return vmModule->types[index]; } VMType loadVMType( VMModule* vmModule, BCType* bcType) { // Need to load type from BC format to VM IROp op = (IROp) bcType->op; switch(bcType->op) { case kIROp_PtrType: { // TODO: need to do some caching! BCPtrType* bcPtrType = (BCPtrType*) bcType; VMPtrTypeImpl vmPtrTypeImpl; vmPtrTypeImpl.op = op; vmPtrTypeImpl.argCount = 1; vmPtrTypeImpl.size = sizeof(void*); vmPtrTypeImpl.alignment = sizeof(void*); vmPtrTypeImpl.base = getType(vmModule, bcPtrType->valueType->id); auto vmPtrType = getType(vmModule->vm, &vmPtrTypeImpl); return vmPtrType; } break; default: { UInt argCount = bcType->argCount; UInt size = sizeof(VMTypeImpl) + argCount * sizeof(VMVal); VMTypeImpl* impl = (VMTypeImpl*) alloca(size); memset(impl, 0, size); impl->op = bcType->op; impl->argCount = (uint32_t)argCount; VMVal* args = (VMVal*) (impl + 1); for(UInt aa = 0; aa < argCount; ++aa) { args[aa] = getVal(vmModule, bcType->getArg(aa)->id); } return getType(vmModule->vm, impl); } UNREACHABLE(SLANG_UNEXPECTED("unimplemented")); UNREACHABLE_RETURN(VMType()); break; } } void* allocateImpl(VM* /*vm*/, UInt size, UInt /*align*/) { void* ptr = malloc(size); memset(ptr, 0, size); return ptr; } void* allocate(VM* vm, VMType type) { return allocateImpl(vm, type.getSize(), type.getAlignment()); } template T* allocate(VM* vm) { return allocateImpl(vm, sizeof(T), alignof(T)); } void* loadVMSymbol( VMModule* vmModule, BCSymbol* bcSymbol) { // Need to load type from BC format to VM auto vm = vmModule->vm; switch(bcSymbol->op) { case kIROp_global_var: { auto type = getType(vmModule, bcSymbol->typeID); assert(type.impl->op == kIROp_PtrType); VMPtrTypeImpl* ptrTypeImpl = (VMPtrTypeImpl*) type.impl; auto valueType = ptrTypeImpl->base; void* varValue = allocate(vm, valueType); void** varPtr = (void**) allocate(vm, type); *varPtr = varValue; return varPtr; } break; case kIROp_global_constant: { auto type = getType(vmModule, bcSymbol->typeID); void* valPtr = allocate(vm, type); return valPtr; } break; case kIROp_Func: { auto bcFunc = (BCFunc*) bcSymbol; VMFunc* vmFunc = loadVMFunc(bcFunc, vmModule); return vmFunc; } break; default: return nullptr; } } VMModule* loadVMModuleInstance( VM* vm, void const* bytecode, size_t /*bytecodeSize*/) { BCHeader* bcHeader = (BCHeader*) bytecode; UInt bcModuleCount = bcHeader->moduleCount; if (bcModuleCount == 0) return nullptr; BCModule* bcModule = bcHeader->modules[0]; UInt symbolCount = bcModule->symbolCount; UInt typeCount = bcModule->typeCount; UInt vmModuleSize = sizeof(VMModule) + symbolCount * sizeof(void*) + typeCount * sizeof(VMType); VMModule* vmModule = (VMModule*)malloc(vmModuleSize); memset(vmModule, 0, vmModuleSize); void** vmSymbols = (void**)(vmModule + 1); VMType* vmTypes = (VMType*)(vmSymbols + symbolCount); vmModule->bcModule = bcModule; vmModule->vm = vm; vmModule->symbols = vmSymbols; vmModule->types = vmTypes; // Initialize types before symbols, since the symbols // will all have types... for(UInt tt = 0; tt < typeCount; ++tt) { BCType* bcType = bcModule->types[tt]; vmTypes[tt] = loadVMType(vmModule, bcType); } // Now we need to initialize all the VM-level symbols // from their BC-level equivalents. for(UInt ss = 0; ss < symbolCount; ++ss) { BCSymbol* bcSymbol = bcModule->symbols[ss]; vmSymbols[ss] = loadVMSymbol(vmModule, bcSymbol); } return vmModule; } void* findGlobalSymbolPtr( VMModule* module, char const* name) { // Okay, we need to search through the available // symbols, looking for one that gives us a name // match. BCModule* bcModule = module->bcModule; UInt symbolCount = bcModule->symbolCount; for(UInt ss = 0; ss < symbolCount; ++ss) { BCSymbol* bcSymbol = bcModule->symbols[ss]; char const* symbolName = bcSymbol->name; if (!symbolName) continue; if(strcmp(symbolName, name) == 0) return getGlobalPtr(module, (uint32_t)ss); } return nullptr; } VMThread* createThread( VM* /*vm*/) { VMThread* thread = new VMThread(); thread->frame = nullptr; return thread; } void beginCall( VMThread* vmThread, VMFunc* vmFunc) { VMFrame* vmFrame = createFrame(vmFunc); vmFrame->parent = vmThread->frame; vmThread->frame = vmFrame; } void setArg( VMThread* vmThread, UInt argIndex, void const* data, size_t size) { // TODO: need all kinds of validation here... void* dest = getRegPtrImpl(vmThread->frame, argIndex); memcpy(dest, data, size); } void resumeThread( VMThread* vmThread) { auto frame = vmThread->frame; auto ip = frame->ip; for(;;) { #if 0 // debugging: frame->ip = ip; dumpVMFrame(frame); #endif auto op = (IROp) decodeUInt(&ip); switch( op ) { case kIROp_Var: { // This instruction represents the `alloca` for a variable of some type. VMType type = decodeType(frame, &ip); UInt argCount = decodeUInt(&ip); void* argPtrs[16] = { 0 }; for( UInt aa = 0; aa < argCount; ++aa ) { void* argPtr = decodeOperandPtr(frame, &ip); argPtrs[aa] = argPtr; } void** destPtr = decodeOperandPtr(frame, &ip); // For now this is a bit silly and simple: the // storage for the variable we are "allocating" // should be right after outer destination, so we can // set it pretty easily. *destPtr = destPtr + 1; } break; case kIROp_Store: { // An ordinary memory store VMType type = decodeType(frame, &ip); void* dest = decodeOperand(frame, &ip); void* src = decodeOperandPtr(frame, &ip); #if 0 fprintf(stderr, "STORE *[%p] = [%p] // size: %d\n", dest, src, (int) type.getSize()); #endif memcpy(dest, src, type.getSize()); } break; case kIROp_Load: { // An ordinary memory store VMType type = decodeType(frame, &ip); void* src = decodeOperand(frame, &ip); void* dest = decodeOperandPtr(frame, &ip); memcpy(dest, src, type.getSize()); } break; case kIROp_BufferLoad: { VMType type = decodeType(frame, &ip); UInt argCount = decodeUInt(&ip); void* argPtrs[16] = { 0 }; for( UInt aa = 0; aa < argCount; ++aa ) { void* argPtr = decodeOperandPtr(frame, &ip); argPtrs[aa] = argPtr; } void* dest = decodeOperandPtr(frame, &ip); char* bufferData = *(char**)argPtrs[0]; uint32_t index = *(uint32_t*)argPtrs[1]; auto size = type.getSize(); char* elementData = bufferData + index*size; memcpy(dest, elementData, size); } break; case kIROp_BufferStore: { VMType resultType = decodeType(frame, &ip); /*UInt argCount = */decodeUInt(&ip); char* bufferData = decodeOperand(frame, &ip); uint32_t index = decodeOperand(frame, &ip); auto srcPtrAndType = decodeOperandPtrAndType(frame, &ip); void* srcPtr = srcPtrAndType.ptr; VMType type = srcPtrAndType.type; auto size = type.getSize(); char* elementData = bufferData + index*size; memcpy(elementData, srcPtr, size); } break; case kIROp_BufferElementRef: { VMType ptrType = decodeType(frame, &ip); VMType type = ((VMPtrTypeImpl*)ptrType.getImpl())->base; UInt argCount = decodeUInt(&ip); void* argPtrs[16] = { 0 }; for( UInt aa = 0; aa < argCount; ++aa ) { void* argPtr = decodeOperandPtr(frame, &ip); argPtrs[aa] = argPtr; } void* dest = decodeOperandPtr(frame, &ip); char* bufferData = *(char**)argPtrs[0]; uint32_t index = *(uint32_t*)argPtrs[1]; auto size = type.getSize(); char* elementData = bufferData + index*size; *(void**)dest = elementData; } break; case kIROp_Call: { VMType type = decodeType(frame, &ip); UInt operandCount = decodeUInt(&ip); // First operand is the callee function VMFunc* func = decodeOperand(frame, &ip); // Okay, we need to create a frame to prepare the call VMFrame* newFrame = createFrame(func); newFrame->parent = frame; // Remaining arguments should populate the // first N registers of the callee UInt argCount = operandCount - 1; for( UInt aa = 0; aa < argCount; ++aa ) { void* argPtr = decodeOperandPtr(frame, &ip); void* regPtr = getRegPtrImpl(newFrame, aa); VMType regType = func->regs[aa].type; memcpy(regPtr, argPtr, regType.getSize()); } // Note that we do *not* try to read off // the destination operand, and instead // leave that to be done during the // return sequence. // // Save the IP we were using in teh current function. // frame->ip = ip; // Now switch over to the callee: // frame = newFrame; ip = newFrame->ip; } break; case kIROp_ReturnVoid: { // Easy case: just jump to the parent frame, // without having to worry about operands. VMFrame* newFrame = frame->parent; vmThread->frame = newFrame; // HACK: we need to know when we are done. // TODO: We should probably have the bottom // of the stack for a thread always point // to a special bytecode sequence that // forces a `yield` op that can handle // the exit from the interpreter, rather // than always take a branch here. if (!newFrame) return; frame = newFrame; ip = frame->ip; } break; case kIROp_ReturnVal: { VMType instType = decodeType(frame, &ip); /*UInt argCount =*/ decodeUInt(&ip); void* argPtr = decodeOperandPtr(frame, &ip); VMFrame* newFrame = frame->parent; vmThread->frame = newFrame; // Note: see the comments above about // this branch. if (!newFrame) return; frame = newFrame; ip = frame->ip; auto destPtrAndType = decodeOperandPtrAndType(frame, &ip); void* destPtr = destPtrAndType.ptr; VMType type = destPtrAndType.type; memcpy(destPtr, argPtr, type.getSize()); } break; case kIROp_loop: case kIROp_unconditionalBranch: { // For now our encoding is very regular, so we can decode without // knowing too much about an instruction... VMType type = decodeType(frame, &ip); UInt argCount = decodeUInt(&ip); Int destinationBlockIndex = decodeSInt(&ip); auto func = frame->func; auto& destinationBlock = func->bcFunc->blocks[destinationBlockIndex]; // We may be passing arguments through to the destination // block, so any remaining operands of the branch instruction // need to be used to fill in the parameter registers of // the destination block. UInt paramCount = destinationBlock.paramCount; // There might be additional operands, because some branches // also include information on merge points and break/continue labels. UInt remainingArgCount = argCount - 1; assert(remainingArgCount >= paramCount); UInt extraArgCount = remainingArgCount - paramCount; for (UInt ee = 0; ee < extraArgCount; ++ee) { decodeOperandPtr(frame, &ip); } auto baseRegIndex = destinationBlock.params - func->bcFunc->regs; for( UInt pp = 0; pp < paramCount; ++pp ) { auto regIndex = baseRegIndex + pp; void* argPtr = decodeOperandPtr(frame, &ip); void* regPtr = getRegPtrImpl(frame, regIndex); VMType regType = func->regs[regIndex].type; memcpy(regPtr, argPtr, regType.getSize()); } // Now simply jump to the destination block. // ip = destinationBlock.code; } break; case kIROp_ifElse: case kIROp_conditionalBranch: { // For now our encoding is very regular, so we can decode without // knowing too much about an instruction... VMType type = decodeType(frame, &ip); UInt argCount = decodeUInt(&ip); bool* condition = decodeOperandPtr(frame, &ip); Int trueBlockID = decodeSInt(&ip); Int falseBlockID = decodeSInt(&ip); for( UInt aa = 4; aa < argCount; ++aa ) { decodeOperandPtr(frame, &ip); } Int destinationBlock = *condition ? trueBlockID : falseBlockID; // TODO: we need to deal with the case of // passing arguments to the block, which // means copying between the registers... // ip = frame->func->bcFunc->blocks[destinationBlock].code; } break; case kIROp_Greater: { // For now our encoding is very regular, so we can decode without // knowing too much about an instruction... VMType resultType = decodeType(frame, &ip); /*UInt argCount = */decodeUInt(&ip); void* argPtrs[16] = { 0 }; auto leftOpnd = decodeOperandPtrAndType(frame, &ip); auto type = leftOpnd.type; auto leftPtr = leftOpnd.ptr; void* rightPtr = decodeOperandPtr(frame, &ip); bool* destPtr = decodeOperandPtr(frame, &ip); switch (type.impl->op) { case kIROp_Int32Type: *destPtr = *(int32_t*)leftPtr > *(int32_t*)rightPtr; break; default: SLANG_UNEXPECTED("comparison op case"); break; } } break; case kIROp_Mul: { VMType type = decodeType(frame, &ip); /*UInt argCount = */decodeUInt(&ip); void* leftPtr = decodeOperandPtr(frame, &ip); void* rightPtr = decodeOperandPtr(frame, &ip); void* destPtr = decodeOperandPtr(frame, &ip); switch (type.impl->op) { case kIROp_Int32Type: *(int32_t*)destPtr = *(int32_t*)leftPtr * *(int32_t*)rightPtr; break; default: SLANG_UNEXPECTED("comparison op case"); break; } } break; case kIROp_Sub: { VMType type = decodeType(frame, &ip); /*UInt argCount = */decodeUInt(&ip); void* leftPtr = decodeOperandPtr(frame, &ip); void* rightPtr = decodeOperandPtr(frame, &ip); void* destPtr = decodeOperandPtr(frame, &ip); switch (type.impl->op) { case kIROp_Int32Type: *(int32_t*)destPtr = *(int32_t*)leftPtr - *(int32_t*)rightPtr; break; default: SLANG_UNEXPECTED("comparison op case"); break; } } break; default: { // For now our encoding is very regular, so we can decode without // knowing too much about an instruction... VMType type = decodeType(frame, &ip); UInt argCount = decodeUInt(&ip); void* argPtrs[16] = { 0 }; for( UInt aa = 0; aa < argCount; ++aa ) { void* argPtr = decodeOperandPtr(frame, &ip); argPtrs[aa] = argPtr; } SLANG_UNEXPECTED("unknown bytecode op"); return; } break; } } } SLANG_API void SlangVMThread_resume( SlangVMThread* thread) { Slang::resumeThread( (Slang::VMThread*) thread); } } // namespace Slang SLANG_API SlangVM* SlangVM_create() { return (SlangVM*) Slang::createVM(); } SLANG_API SlangVMModule* SlangVMModule_load( SlangVM* vm, void const* bytecode, size_t bytecodeSize) { return (SlangVMModule*) Slang::loadVMModuleInstance( (Slang::VM*) vm, bytecode, bytecodeSize); } SLANG_API void* SlangVMModule_findGlobalSymbolPtr( SlangVMModule* module, char const* name) { return (SlangVMFunc*) Slang::findGlobalSymbolPtr( (Slang::VMModule*) module, name); } SLANG_API SlangVMThread* SlangVMThread_create( SlangVM* vm) { return (SlangVMThread*)Slang::createThread( (Slang::VM*) vm); } SLANG_API void SlangVMThread_beginCall( SlangVMThread* thread, SlangVMFunc* func) { Slang::beginCall( (Slang::VMThread*) thread, (Slang::VMFunc*) func); } SLANG_API void SlangVMThread_setArg( SlangVMThread* thread, SlangUInt argIndex, void const* data, size_t size) { Slang::setArg( (Slang::VMThread*) thread, argIndex, data, size); } SLANG_API void SlangVMThread_resume( SlangVMThread* thread) { Slang::resumeThread( (Slang::VMThread*) thread); }