diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-12-10 12:42:15 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-12-10 12:42:15 -0800 |
| commit | b2997170df7cc2703de714a946a38dc35058e7f8 (patch) | |
| tree | bbd19208ba07a2f45a2c25f28f6cf77be16f0b49 /source/slang/bytecode.cpp | |
| parent | 32f57c30cfce1681f5fe617e4fe230e88eb7b840 (diff) | |
Remove the "VM" and "bytecode" features (#745)
* Remove the "VM" and "bytecode" features
The "bytecode" in `bc.{h,cpp}` was an initial attempt at a serialized encoding for the Slang IR, but we now have the `ir-serialize.{h,cpp}` approach which was has been kept up to date much better.
Similarly, the "VM" in `vm.{h,cpp}` was intended to be a system for interpreting Slang code in the bytecode format directly (so that you could load and evaluate code in a Slang module in a lightweight fashion). This never got used past a single test, which we eventually disabled.
There are good ideas in some of this code, but at this point the implementations have bit-rotted to a point where trying to maintain it is more costly than it would be to re-created it if/when we ever decide these features are important again.
* fixup: remove slang-eval-test from Makefile
Diffstat (limited to 'source/slang/bytecode.cpp')
| -rw-r--r-- | source/slang/bytecode.cpp | 1071 |
1 files changed, 0 insertions, 1071 deletions
diff --git a/source/slang/bytecode.cpp b/source/slang/bytecode.cpp deleted file mode 100644 index 7ceb06cdc..000000000 --- a/source/slang/bytecode.cpp +++ /dev/null @@ -1,1071 +0,0 @@ -#include "bytecode.h" - -// Implementation of the Slang bytecode (BC) -// (most notably including conversion from IR to BC) - -#include "compiler.h" -#include "ir.h" -#include "ir-insts.h" -#include "lower-to-ir.h" - -namespace Slang -{ -struct SharedBytecodeGenerationContext; - -// Representation of a `BCPtr<T>` during actual bytecode generation. -// This representation is to deal with the fact that the actual -// storage for the bytecode data might get reallocated during emission -// so that we need to be careful and not work with raw `BCPtr<T>`. -template<typename T> -struct BytecodeGenerationPtr -{ - UInt offset; - SharedBytecodeGenerationContext* sharedContext; - - BytecodeGenerationPtr() - : sharedContext(nullptr) - , offset(0) - {} - - - BytecodeGenerationPtr( - SharedBytecodeGenerationContext* sharedContext, - UInt offset) - : sharedContext(sharedContext) - , offset(offset) - {} - - BytecodeGenerationPtr( - BytecodeGenerationPtr<T> const& ptr) - : sharedContext(ptr.sharedContext) - , offset(ptr.offset) - {} - - template<typename U> - BytecodeGenerationPtr( - BytecodeGenerationPtr<U> const& ptr, - typename EnableIf<IsConvertible<T*, U*>::Value, void>::type * = 0) - : sharedContext(ptr.sharedContext) - , offset(ptr.offset) - {} - - template<typename U> - BytecodeGenerationPtr<U> bitCast() const - { - return BytecodeGenerationPtr<U>(sharedContext, offset); - } - - operator BCPtr<T>() const - { - return BCPtr<T>(getPtr()); - } - - T* operator->() const - { - return getPtr(); - } - - T& operator*() const - { - return *getPtr(); - } - - T& operator[](UInt index) const - { - return getPtr()[index]; - } - - BytecodeGenerationPtr<T> operator+(Int index) const - { - Int delta = index * sizeof(T); - UInt newOffset = offset + delta; - return BytecodeGenerationPtr<T>( - sharedContext, - newOffset); - } - - T* getPtr() const; -}; - -#if 0 -template<typename T> -void BCPtr<T>::operator=(BytecodeGenerationPtr<T> const& ptr) -{ - fprintf(stderr, "0x%p: operator=BGP 0x%p\n", this, ptr.getPtr()); - *this = ptr.getPtr(); -} -#endif - -struct SharedBytecodeGenerationContext -{ - // The final generated bytecode stream - List<uint8_t> bytecode; - - // Map from an IR value to a global entity - // that encodes it: - Dictionary<IRInst*, BCConst> mapValueToGlobal; - - // Types that have been emitted - List<BytecodeGenerationPtr<BCType>> bcTypes; - Dictionary<IRType*, UInt> mapTypeToID; - - // Compile-time constant values that need - // to be emitted... - List<IRInst*> constants; -}; - -struct BytecodeGenerationContext -{ - SharedBytecodeGenerationContext* shared; - - // The bytecode of the current symbol being - // output. - List<uint8_t> currentBytecode; - - // The function that is in scope for this context - IRFunc* currentIRFunc; - - // Counter for global symbols that have been assigned - // so that they can be used by this function - List<BCConst> remappedGlobalSymbols; - - // Map an instruction to its ID for use local - // to the current context - Dictionary<IRInst*, Int> mapInstToLocalID; -}; - -template<typename T> -T* BytecodeGenerationPtr<T>::getPtr() const -{ - if(!sharedContext) return nullptr; - return (T*)(sharedContext->bytecode.Buffer() + offset); -} - - -BCPtr<void>::RawVal allocateRaw( - BytecodeGenerationContext* context, - size_t size, - size_t alignment) -{ - size_t currentOffset = context->shared->bytecode.Count(); - - size_t beginOffset = (currentOffset + (alignment-1)) & ~(alignment-1); - - size_t endOffset = beginOffset + size; - - for(size_t ii = currentOffset; ii < endOffset; ++ii) - context->shared->bytecode.Add(0); - - return (BCPtr<void>::RawVal)beginOffset; -} - -template<typename T> -BytecodeGenerationPtr<T> allocate( - BytecodeGenerationContext* context) -{ - return BytecodeGenerationPtr<T>(context->shared, allocateRaw(context, sizeof(T), alignof(T))); -} - -template<typename T> -BytecodeGenerationPtr<T> allocateArray( - BytecodeGenerationContext* context, - UInt count) -{ - return BytecodeGenerationPtr<T>(context->shared, allocateRaw(context, count * sizeof(T), alignof(T))); -} - -template<typename T> -BytecodeGenerationPtr<T> getPtr( - BytecodeGenerationContext* context) -{ - return BytecodeGenerationPtr<T>(context->shared, context->shared->bytecode.Count()); -} - - -void encodeUInt8( - BytecodeGenerationContext* context, - uint8_t value) -{ - context->currentBytecode.Add(value); -} - -void encodeUInt( - BytecodeGenerationContext* context, - UInt value) -{ - if( value < 128 ) - { - encodeUInt8(context, (uint8_t)value); - return; - } - - uint8_t bytes[16]; - UInt count = 0; - - for(;;) - { - UInt index = count++; - bytes[index] = value & 0x7F; - value = value >> 7; - if (!value) - break; - - bytes[index] |= 0x80; - } - - UInt index = count; - while (index--) - { - encodeUInt8(context, bytes[index]); - } -} - -void encodeSInt( - BytecodeGenerationContext* context, - Int value) -{ - UInt uValue; - if( value < 0 ) - { - uValue = (~UInt(value) << 1) | 1; - } - else - { - uValue = UInt(value) << 1; - } - - encodeUInt(context, uValue); -} - -BCConst getGlobalValue( - BytecodeGenerationContext* context, - IRInst* value) -{ - { - BCConst bcConst; - if (context->shared->mapValueToGlobal.TryGetValue(value, bcConst)) - return bcConst; - } - // Next we need to check for things that can be mapped to - // global IDs on the fly. - - switch( value->op ) - { - case kIROp_IntLit: - { - UInt constID = context->shared->constants.Count(); - context->shared->constants.Add(value); - - BCConst bcConst; - bcConst.flavor = kBCConstFlavor_Constant; - bcConst.id = (uint32_t)constID; - - context->shared->mapValueToGlobal.Add(value, bcConst); - - return bcConst; - } - break; - - default: - break; - } - - SLANG_UNEXPECTED("no ID for inst"); - { - UNREACHABLE(BCConst bcConst); - UNREACHABLE(bcConst.flavor = (BCConstFlavor)-1); - UNREACHABLE(bcConst.id = -9999); - UNREACHABLE_RETURN(bcConst); - } -} - -Int getLocalID( - BytecodeGenerationContext* context, - IRInst* value) -{ - Int localID = 0; - if( context->mapInstToLocalID.TryGetValue(value, localID) ) - { - return localID; - } - - BCConst bcConst = getGlobalValue(context, value); - UInt remappedSymbolIndex = context->remappedGlobalSymbols.Count(); - context->remappedGlobalSymbols.Add(bcConst); - - localID = ~remappedSymbolIndex; - context->mapInstToLocalID.Add(value, localID); - return localID; -} - -void encodeOperand( - BytecodeGenerationContext* context, - IRInst* operand) -{ - auto id = getLocalID(context, operand); - encodeSInt(context, id); -} - -uint32_t getTypeID( - BytecodeGenerationContext* context, - IRType* type); - -void encodeOperand( - BytecodeGenerationContext* context, - IRType* type) -{ - encodeUInt(context, getTypeID(context, type)); -} - -bool opHasResult(IRInst* inst) -{ - auto type = inst->getDataType(); - if (!type) return false; - - // As a bit of a hack right now, we need to check whether - // the function returns the distinguished `Void` type, - // since that is conceptually the same as "not returning - // a value." - if(type->op == kIROp_VoidType) - return false; - - return true; -} - -void generateBytecodeForInst( - BytecodeGenerationContext* context, - IRInst* inst) -{ - // We are generating bytecode for a local instruction - // inside a function or similar context. - switch( inst->op ) - { - default: - { - // As a default case, we will assume that bytecode ops - // and the IR's internal opcodes are the same, and then - // encode the necessary extra info: - // - - auto operandCount = inst->getOperandCount(); - encodeUInt(context, inst->op); - encodeOperand(context, inst->getDataType()); - encodeUInt(context, operandCount); - for( UInt aa = 0; aa < operandCount; ++aa ) - { - encodeOperand(context, inst->getOperand(aa)); - } - - if (!opHasResult(inst)) - { - // This instructions has no type, so don't emit a destination - } - else - { - // The instruction can be encoded - // as its own operand for the destination. - encodeOperand(context, inst); - } - } - break; - - case kIROp_ReturnVoid: - // Trivial encoding here - encodeUInt(context, inst->op); - break; - - case kIROp_IntLit: - { - auto ii = (IRConstant*) inst; - encodeUInt(context, ii->op); - encodeOperand(context, ii->getDataType()); - - // TODO: probably want distinct encodings - // for signed vs. unsigned here. - encodeUInt(context, UInt(ii->value.intVal)); - - // destination: - encodeOperand(context, inst); - } - break; - - case kIROp_FloatLit: - { - auto cInst = (IRConstant*) inst; - encodeUInt(context, cInst->op); - encodeOperand(context, cInst->getDataType()); - - static const UInt size = sizeof(IRFloatingPointValue); - unsigned char buffer[size]; - memcpy(buffer, &cInst->value.floatVal, sizeof(buffer)); - - for(UInt ii = 0; ii < size; ++ii) - { - encodeUInt8(context, buffer[ii]); - } - - // destination: - encodeOperand(context, inst); - } - break; - - case kIROp_BoolLit: - { - auto ii = (IRConstant*) inst; - encodeUInt(context, ii->op); - encodeUInt(context, ii->value.intVal ? 1 : 0); - - // destination: - encodeOperand(context, inst); - } - break; - -#if 0 - case kIROp_Func: - { - encodeUInt(context, inst->op); - - // We just want to encode the ID for the function - // symbol data, and then do the rest on the decode side - UInt nestedID = 0; - context->mapInstToNestedID.TryGetValue(inst, nestedID); - encodeUInt(context, nestedID); - - // destination: - encodeOperand(context, inst); - } - break; -#endif - - case kIROp_Store: - { - encodeUInt(context, inst->op); - - // We need to encode the type being stored, to make - // our lives easier. - encodeOperand(context, inst->getOperand(1)->getDataType()); - encodeOperand(context, inst->getOperand(0)); - encodeOperand(context, inst->getOperand(1)); - } - break; - - case kIROp_Load: - { - encodeUInt(context, inst->op); - encodeOperand(context, inst->getDataType()); - encodeOperand(context, inst->getOperand(0)); - encodeOperand(context, inst); - } - break; - } -} - -BytecodeGenerationPtr<BCType> emitBCType( - BytecodeGenerationContext* context, - IRType* type, - IROp op, - BytecodeGenerationPtr<uint8_t> const* args, - UInt argCount) -{ - UInt size = sizeof(BCType) - + argCount * sizeof(BCPtr<void>); - - BytecodeGenerationPtr<uint8_t> bcAllocation( - context->shared, - allocateRaw(context, size, alignof(BCPtr<void>))); - - BytecodeGenerationPtr<BCType> bcType = bcAllocation.bitCast<BCType>(); - auto bcArgs = (bcType + 1).bitCast<BCPtr<uint8_t>>(); - - bcType->op = op; - bcType->argCount = (uint32_t)argCount; - - for(UInt aa = 0; aa < argCount; ++aa) - { - bcArgs[aa] = args[aa]; - } - - UInt id = context->shared->bcTypes.Count(); - context->shared->mapTypeToID.Add(type, id); - context->shared->bcTypes.Add(bcType); - bcType->id = (uint32_t)id; - - return bcType; -} - -BytecodeGenerationPtr<BCType> emitBCVarArgType( - BytecodeGenerationContext* context, - IRType* type, - IROp op, - List<BytecodeGenerationPtr<uint8_t>> args) -{ - return emitBCType(context, type, op, args.Buffer(), args.Count()); -} - -BytecodeGenerationPtr<BCType> emitBCType( - BytecodeGenerationContext* context, - IRType* type, - IROp op) -{ - return emitBCType(context, type, op, nullptr, 0); -} - -BytecodeGenerationPtr<BCType> emitBCType( - BytecodeGenerationContext* context, - IRType* type); - -// Emit a `BCType` representation for the given `Type` -BytecodeGenerationPtr<BCType> emitBCTypeImpl( - BytecodeGenerationContext* context, - IRType* type) -{ - // A NULL type is interpreted as equivalent to `Void` for now. - if( !type ) - { - return emitBCType(context, type, kIROp_VoidType); - } - - List<BytecodeGenerationPtr<uint8_t>> operands; - UInt operandCount = type->getOperandCount(); - for (UInt ii = 0; ii < operandCount; ++ii) - { - operands.Add(emitBCType(context, (IRType*) type->getOperand(ii)).bitCast<uint8_t>()); - } - return emitBCVarArgType(context, type, type->op, operands); -} - -BytecodeGenerationPtr<BCType> emitBCType( - BytecodeGenerationContext* context, - IRType* type) -{ - auto canonical = type->getCanonicalType(); - UInt id = 0; - if(context->shared->mapTypeToID.TryGetValue(canonical, id)) - { - return context->shared->bcTypes[id]; - } - - BytecodeGenerationPtr<BCType> bcType = emitBCTypeImpl(context, canonical); - return bcType; -} - -uint32_t getTypeID( - BytecodeGenerationContext* context, - IRType* type) -{ - // We have a type, and we need to emit it (if we haven't - // already) and return its index in the global type table. - BytecodeGenerationPtr<BCType> bcType = emitBCType(context, type); - return bcType->id; -} - -uint32_t getTypeIDForGlobalSymbol( - BytecodeGenerationContext* context, - IRInst* inst) -{ - auto type = inst->getDataType(); - if(!type) - return 0; - - return getTypeID(context, type); -} - -BytecodeGenerationPtr<char> allocateString( - BytecodeGenerationContext* context, - char const* data, - UInt size) -{ - BytecodeGenerationPtr<char> ptr = allocateArray<char>(context, size + 1); - memcpy(ptr.getPtr(), data, size); - return ptr; -} - -BytecodeGenerationPtr<char> allocateString( - BytecodeGenerationContext* context, - String const& str) -{ - return allocateString(context, - str.Buffer(), - str.Length()); -} - -BytecodeGenerationPtr<char> allocateString( - BytecodeGenerationContext* context, - Name* name) -{ - return allocateString(context, name->text); -} - -BytecodeGenerationPtr<char> tryGenerateNameForSymbol( - BytecodeGenerationContext* context, - IRGlobalValue* inst) -{ - // TODO: this is gross, and the IR should probably have - // a more direct means of querying a name for a symbol. - if (auto highLevelDeclDecoration = inst->findDecoration<IRHighLevelDeclDecoration>()) - { - auto decl = highLevelDeclDecoration->decl; - if (auto reflectionNameMod = decl->FindModifier<ParameterGroupReflectionName>()) - { - return allocateString(context, reflectionNameMod->name); - } - else if(auto name = decl->nameAndLoc.name) - { - return allocateString(context, name); - } - } - - return BytecodeGenerationPtr<char>(); -} - -// Generate a `BCSymbol` that can represent a global value. -BytecodeGenerationPtr<BCSymbol> generateBytecodeSymbolForInst( - BytecodeGenerationContext* context, - IRGlobalValue* inst) -{ - switch( inst->op ) - { - case kIROp_Func: - { - auto irFunc = (IRFunc*) inst; - BytecodeGenerationPtr<BCFunc> bcFunc = allocate<BCFunc>(context); - - bcFunc->op = inst->op; - bcFunc->typeID = getTypeIDForGlobalSymbol(context, inst); - - BytecodeGenerationContext subContextStorage; - BytecodeGenerationContext* subContext = &subContextStorage; - subContext->shared = context->shared; - subContext->currentIRFunc = irFunc; - - // First we need to enumerate our basic blocks, so that they - // can reference one another (basic blocks can forward reference - // blocks that haven't been seen yet). - // - // Note: we allow the IDs of blocks to overlap with ordinary - // "register" numbers, because there is no case where an operand - // could be either a block or an ordinary register. - // - UInt blockCounter = 0; - for( auto bb = irFunc->getFirstBlock(); bb; bb = bb->getNextBlock() ) - { - Int blockID = Int(blockCounter++); - subContext->mapInstToLocalID.Add(bb, blockID); - } - UInt blockCount = blockCounter; - - // Allocate the array of block objects to be stored in the - // bytecode file. - auto bcBlocks = allocateArray<BCBlock>(context, blockCount); - bcFunc->blockCount = (uint32_t)blockCount; - bcFunc->blocks = bcBlocks; - - // Now loop through the blocks again, and allocate the storage - // for any parameters, variables, or registers used inside - // each block. - // - // We'll count in a first pass, and then fill things in - // using a second pass - Int regCounter = 0; - blockCounter = 0; - for( auto bb = irFunc->getFirstBlock(); bb; bb = bb->getNextBlock() ) - { - UInt blockID = blockCounter++; - UInt paramCount = 0; - - for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() ) - { - switch( ii->op ) - { - default: - // Default behavior: if an op has a result, - // then it needs a register to store it. - if(opHasResult(ii)) - { - regCounter++; - } - break; - - case kIROp_Param: - // A parameter always uses a register. - regCounter++; - // - // We also want to keep a count of the parameters themselves. - paramCount++; - break; - - case kIROp_Var: - // A `var` (`alloca`) node needs two registers: - // one to hold the actual storage, and another - // to hold the pointer. - regCounter += 2; - break; - } - } - - bcBlocks[blockID].paramCount = (uint32_t)paramCount; - } - - // Okay, we've counted how many registers we need for each block, - // and now we can allocate the contiguous array we will use. - UInt regCount = regCounter; - auto bcRegs = allocateArray<BCReg>(context, regCount); - - bcFunc->regCount = (uint32_t)regCount; - bcFunc->regs = bcRegs; - - // Now we will loop over things again to fill in the information - // on all these registers. - - regCounter = 0; - blockCounter = 0; - for( auto bb = irFunc->getFirstBlock(); bb; bb = bb->getNextBlock() ) - { - UInt blockID = blockCounter++; - - // Loop over the instruction in the block, to allocate registers - // for them. The parameters of a block will always be the first - // N instructions in the block, so they will always get the - // first N registers in that block. Similarly, the entry block - // is always the first block, so that the parameters of the function - // will always be the first N registers. - // - bcBlocks[blockID].params = bcRegs + regCounter; - for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() ) - { - switch(ii->op) - { - default: - // For a parameter, or an ordinary instruction with - // a result, allocate it here. - if( opHasResult(ii) ) - { - Int localID = regCounter++; - subContext->mapInstToLocalID.Add(ii, localID); - - bcRegs[localID].op = ii->op; -#if 0 - bcRegs[localID].name = tryGenerateNameForSymbol(context, ii); -#endif - bcRegs[localID].previousVarIndexPlusOne = (uint32_t)localID; - bcRegs[localID].typeID = getTypeIDForGlobalSymbol(context, ii); - } - break; - - case kIROp_Var: - // As handled in the earlier loop, we are - // allocating *two* locations for each `var` - // instruction. The first of these will be - // the actual pointer value, while the second - // will be the storage for the variable value. - { - Int localID = regCounter; - regCounter += 2; - - subContext->mapInstToLocalID.Add(ii, localID); - bcRegs[localID].op = ii->op; -#if 0 - bcRegs[localID].name = tryGenerateNameForSymbol(context, ii); -#endif - bcRegs[localID].previousVarIndexPlusOne = (uint32_t)localID; - bcRegs[localID].typeID = getTypeIDForGlobalSymbol(context, ii); - - bcRegs[localID+1].op = ii->op; - bcRegs[localID+1].previousVarIndexPlusOne = (uint32_t)localID+1; - bcRegs[localID+1].typeID = getTypeID(context, - (as<IRPtrType>(ii->getDataType()))->getValueType()); - } - break; - } - } - } - assert((UInt)regCounter == regCount); - - // Now that we've allocated our blocks and our registers - // we can go through the actual process of emitting instructions. Hooray! - blockCounter = 0; - - // Offset of each basic block from the start of the code - // for the current funciton. - List<UInt> blockOffsets; - for( auto bb = irFunc->getFirstBlock(); bb; bb = bb->getNextBlock() ) - { - blockCounter++; - - // Get local bytecode offset for current block. - UInt blockOffset = subContext->currentBytecode.Count(); - blockOffsets.Add( blockOffset ); - - for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() ) - { - // What we do with each instruction depends a bit on the - // kind of instruction it is. - switch( ii->op ) - { - default: - // For most instructions we just emit their bytecode - // ops directly. - generateBytecodeForInst(subContext, ii); - break; - - case kIROp_Param: - // Don't actually emit code for these, because - // there isn't really anything to *execute* - // - // Note that we *do* allow the `var` nodes - // to be executed, just because they need - // to set up a register with the pointer value. - break; - } - } - } - - - // We've collected bytecode for the instruction stream - // into a sub-context, so we can now append that code. - UInt byteCount = subContext->currentBytecode.Count(); - BytecodeGenerationPtr<uint8_t> bytes = allocateArray<uint8_t>(context, byteCount); - memcpy(&bytes[0], subContext->currentBytecode.Buffer(), byteCount); - - // Now that we've allocated the storage, we can write - // the bytecode pointers into the blocks. - blockCounter = 0; - for( auto bb = irFunc->getFirstBlock(); bb; bb = bb->getNextBlock() ) - { - UInt blockID = blockCounter++; - bcBlocks[blockID].code = bytes + blockOffsets[blockID]; - } - - // Finally, after emitting all the instructions we can - // build a table of global symbols taht need to be - // imported into the current function as constants. - UInt constCount = subContext->remappedGlobalSymbols.Count(); - auto bcConsts = allocateArray<BCConst>(context, constCount); - - bcFunc->constCount = (uint32_t)constCount; - bcFunc->consts = bcConsts; - - for( UInt cc = 0; cc < constCount; ++cc ) - { - bcConsts[cc] = subContext->remappedGlobalSymbols[cc]; - } - - return bcFunc; - } - break; - - case kIROp_GlobalVar: - case kIROp_GlobalConstant: - { - auto bcVar = allocate<BCSymbol>(context); - - bcVar->op = inst->op; - bcVar->typeID = getTypeID(context, inst->getFullType()); - - // TODO: actually need to intialize with body instructions - - return bcVar; - } - break; - - default: - // Most instructions don't need a custom representation. - return BytecodeGenerationPtr<BCSymbol>(); - } -} - -BytecodeGenerationPtr<BCModule> generateBytecodeForModule( - BytecodeGenerationContext* context, - IRModule* irModule) -{ - if (!irModule) - { - // Not IR module? Then return a null pointer. - return BytecodeGenerationPtr<BCModule>(); - } - - // A module in the bytecode is mostly just a list of the - // global symbols in the module. - // - // TODO: we need to be careful and recognize the distinction - // between the global symbols in the *AST* module, vs. those - // symbols which are effectively global in the *IR* module. - // - // We probably need to store these distinctly, since we - // need the AST global symbols for reflection, and then - // also to reconstruct the AST on load when importing a - // serialized module. We then need the global IR symbols - // to use when linking, to quickly resolve things without - // needing any semantic knowledge of nesting at the AST level. - // - auto bcModule = allocate<BCModule>(context); - - // We need to compute how many "registers" to allocate - // for the module, where the registers represent the - // values being computed at the global scope. - UInt symbolCount = 0; - for(auto ii : irModule->getGlobalInsts()) - { - auto gv = as<IRGlobalValue>(ii); - if (!gv) - continue; - - Int globalID = Int(symbolCount++); - - // Ensure that local code inside functions can see these symbols - BCConst bcConst; - bcConst.flavor = kBCConstFlavor_GlobalSymbol; - bcConst.id = (uint32_t)globalID; - context->shared->mapValueToGlobal.Add(gv, bcConst); - - // In the global scope, global IDs are also the local IDs - context->mapInstToLocalID.Add(gv, globalID); - } - - auto bcSymbols = allocateArray<BCPtr<BCSymbol>>(context, symbolCount); - - bcModule->symbolCount = (uint32_t)symbolCount; - bcModule->symbols = bcSymbols; - - for(auto ii : irModule->getGlobalInsts()) - { - auto gv = as<IRGlobalValue>(ii); - if (!gv) - continue; - - UInt symbolIndex = *context->mapInstToLocalID.TryGetValue(gv); - - auto bcSymbol = generateBytecodeSymbolForInst(context, gv); - if (!bcSymbol.getPtr()) - continue; - - auto name = tryGenerateNameForSymbol(context, gv); - bcSymbol->name = name; - - bcSymbols[symbolIndex] = bcSymbol; - } - - // At this point we should have identified all the literals we need: - UInt constantCount = context->shared->constants.Count(); - auto bcConstants = allocateArray<BCConstant>(context, constantCount); - bcModule->constantCount = (uint32_t)constantCount; - bcModule->constants = bcConstants; - - for(UInt cc = 0; cc < constantCount; ++cc) - { - auto irConstant = (IRConstant*) context->shared->constants[cc]; - bcConstants[cc].op = irConstant->op; - bcConstants[cc].typeID = getTypeID(context, irConstant->getFullType()); - - switch(irConstant->op) - { - case kIROp_IntLit: - { - auto ptr = allocate<IRIntegerValue>(context); - *ptr = irConstant->value.intVal; - bcConstants[cc].ptr = ptr.bitCast<uint8_t>(); - } - break; - - default: - break; - } - - } - - // At this point we should have collected all the types we need: - UInt typeCount = context->shared->bcTypes.Count(); - auto bcTypes = allocateArray<BCPtr<BCType>>(context, typeCount); - bcModule->typeCount = (uint32_t)typeCount; - bcModule->types = bcTypes; - - for(UInt tt = 0; tt < typeCount; ++tt) - { - bcTypes[tt] = context->shared->bcTypes[tt]; - } - - - return bcModule; -} - -void generateBytecodeContainer( - BytecodeGenerationContext* context, - CompileRequest* compileReq) -{ - // Header must be the very first thing in the bytecode stream - BytecodeGenerationPtr<BCHeader> header = allocate<BCHeader>(context); - - memcpy(header->magic, "slang\0bc", sizeof(header->magic)); - header->version = 0; - - // TODO: Need to generate BC representation of all the public/exported - // declrations in the translation units, so that they can be used to - // resolve depenencies downstream. - - // TODO: Need to dump BC representation of compiled kernel codes - // for each specified code-generation target. - - List<BytecodeGenerationPtr<BCModule>> bcModulesList; - for (auto translationUnitReq : compileReq->translationUnits) - { - auto bcModule = generateBytecodeForModule(context, translationUnitReq->irModule); - bcModulesList.Add(bcModule); - } - - UInt bcModuleCount = bcModulesList.Count(); - header->moduleCount = (uint32_t)bcModuleCount; - - auto bcModules = allocateArray<BCPtr<BCModule>>(context, bcModuleCount); - header->modules = bcModules; - for(UInt ii = 0; ii < bcModuleCount; ++ii) - { - bcModules[ii] = bcModulesList[ii]; - } -} - -void generateBytecodeForCompileRequest( - CompileRequest* compileReq) -{ - SharedBytecodeGenerationContext sharedContext; - - BytecodeGenerationContext context; - context.shared = &sharedContext; - - generateBytecodeContainer(&context, compileReq); - - compileReq->generatedBytecode = sharedContext.bytecode; -} - -// TODO: Need to support IR emit at the whole-module/compile-request -// level, and not just for individual entry points. -#if 0 -List<uint8_t> emitSlangIRForEntryPoint( - EntryPointRequest* entryPoint) -{ - auto compileRequest = entryPoint->compileRequest; - auto irModule = lowerEntryPointToIR( - entryPoint, - compileRequest->layout.Ptr(), - // TODO: we need to pick the target more carefully here - CodeGenTarget::HLSL); - -#if 0 - String irAsm = getSlangIRAssembly(irModule); - fprintf(stderr, "%s\n", irAsm.Buffer()); -#endif - - // Now we need to encode that IR into a binary format - // for transmission/serialization/etc. - - SharedBytecodeGenerationContext sharedContext; - - BytecodeGenerationContext context; - context.shared = &sharedContext; - - generateBytecodeStream(&context, irModule); - - return sharedContext.bytecode; -} -#endif - -} // namespace Slang |
