summaryrefslogtreecommitdiffstats
path: root/source/slang/bytecode.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2018-12-10 12:42:15 -0800
committerGitHub <noreply@github.com>2018-12-10 12:42:15 -0800
commitb2997170df7cc2703de714a946a38dc35058e7f8 (patch)
treebbd19208ba07a2f45a2c25f28f6cf77be16f0b49 /source/slang/bytecode.cpp
parent32f57c30cfce1681f5fe617e4fe230e88eb7b840 (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.cpp1071
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