From 54f016e7ef36b7505bf47d188cf4b7e1fdc443a4 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Wed, 4 Oct 2017 13:54:25 -0700 Subject: IR: overhaul IR design/implementation (#195) * IR: overhaul IR design/implementation Closes #192 Closes #188 This is a major overhaul of how the IR is implemented, with the primary goal of just using the AST-level type representation as the IR's type representation, rather than inventing an entire shadow set of types (as captured in issue #192). One consequence of this choice is that types in the IR are no longer explicit "instructions" and are not represented as ordinary operands (so a bunch of `+ 1` cases end up going away when enumerating ordinary operands). Along the way I also got rid of the embedded IDs in the IR (issue #188) because this wasn't too hard to deal with at the same time. Another related change was to split the `IRValue` and `IRInst` cases, so that there are values that are not also instructions. Non-instruction values are now used to represent literals, references to declarations, and would eventually be used for an `undef` value if we need one. IR functions, global variables, and basic blocks are all values (because they can appear as operands), but not instructions. The main benefit of this approach is that the top-level structure of a bytecode (BC) module is much simpler to understand and walk, and BC-level types are represented much more directly (such that we could conceivably use them for reflection soon). * fixup: 64-bit build fix * fixup: try to silence clang's pedantic dependent-type errors * fixup: bug in VM loading of constants --- source/slang/bytecode.cpp | 555 +++++++++++++++++++++++++++++++--------------- 1 file changed, 380 insertions(+), 175 deletions(-) (limited to 'source/slang/bytecode.cpp') diff --git a/source/slang/bytecode.cpp b/source/slang/bytecode.cpp index e412a5b94..854d9bf34 100644 --- a/source/slang/bytecode.cpp +++ b/source/slang/bytecode.cpp @@ -19,8 +19,8 @@ struct SharedBytecodeGenerationContext; template struct BytecodeGenerationPtr { - SharedBytecodeGenerationContext* sharedContext; UInt offset; + SharedBytecodeGenerationContext* sharedContext; BytecodeGenerationPtr() : sharedContext(nullptr) @@ -49,31 +49,40 @@ struct BytecodeGenerationPtr , offset(ptr.offset) {} - operator BCPtr() + template + BytecodeGenerationPtr bitCast() const + { + return BytecodeGenerationPtr(sharedContext, offset); + } + + operator BCPtr() const { return BCPtr(getPtr()); } - T* operator->() + T* operator->() const { return getPtr(); } - T& operator*() + T& operator*() const { return *getPtr(); } - T& operator[](UInt index) + T& operator[](UInt index) const { return getPtr()[index]; } - BytecodeGenerationPtr operator+(Int index) + BytecodeGenerationPtr operator+(Int index) const { + UInt size = sizeof(T); + Int delta = index * sizeof(T); + UInt newOffset = offset + delta; return BytecodeGenerationPtr( sharedContext, - offset + index*sizeof(T)); + newOffset); } T* getPtr() const; @@ -93,14 +102,27 @@ struct SharedBytecodeGenerationContext // The final generated bytecode stream List bytecode; - // Map from a global symbol to its global ID - Dictionary mapGlobalSymbolToGLobalID; + // Map from an IR value to a global entity + // that encodes it: + Dictionary mapValueToGlobal; + + // Types that have been emitted + List> bcTypes; + Dictionary mapTypeToID; + + // Compile-time constant values that need + // to be emitted... + List constants; }; struct BytecodeGenerationContext { SharedBytecodeGenerationContext* shared; + // The bytecode of the current symbol being + // output. + List currentBytecode; + // The function that is in scope for this context IRFunc* currentIRFunc; @@ -110,11 +132,7 @@ struct BytecodeGenerationContext // Map an instruction to its ID for use local // to the current context - Dictionary mapInstToLocalID; - - // Map an instruction to the ID for its auxiliary - // symbol data - Dictionary mapInstToNestedID; + Dictionary mapInstToLocalID; }; template @@ -169,7 +187,7 @@ void encodeUInt8( BytecodeGenerationContext* context, uint8_t value) { - context->shared->bytecode.Add(value); + context->currentBytecode.Add(value); } void encodeUInt( @@ -220,50 +238,98 @@ void encodeSInt( encodeUInt(context, uValue); } -Int getLocalID( +BCConst getGlobalValue( BytecodeGenerationContext* context, - IRInst* inst) + IRValue* value) { - Int localID = 0; - if( context->mapInstToLocalID.TryGetValue(inst, localID) ) - { - return localID; - } + 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. - Int globalID = 0; - if( context->shared->mapGlobalSymbolToGLobalID.TryGetValue(inst, globalID) ) + switch( value->op ) { - BCConst bcConst; - bcConst.globalID = globalID; + case kIROp_IntLit: + { + UInt constID = context->shared->constants.Count(); + context->shared->constants.Add(value); - UInt remappedSymbolIndex = context->remappedGlobalSymbols.Count(); - context->remappedGlobalSymbols.Add(bcConst); + BCConst bcConst; + bcConst.flavor = kBCConstFlavor_Constant; + bcConst.id = constID; - localID = ~remappedSymbolIndex; - context->mapInstToLocalID.Add(inst, localID); - return localID; + context->shared->mapValueToGlobal.Add(value, bcConst); + + return bcConst; + } + break; + + default: + break; } SLANG_UNEXPECTED("no ID for inst"); - return -9999; + bcConst.flavor = (BCConstFlavor) -1; + bcConst.id = -9999; + return bcConst; +} + +Int getLocalID( + BytecodeGenerationContext* context, + IRValue* 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) + IRValue* operand) { auto id = getLocalID(context, operand); encodeSInt(context, id); } -bool opHasResult(IRInst* inst) +uint32_t getTypeID( + BytecodeGenerationContext* context, + Type* type); + +void encodeOperand( + BytecodeGenerationContext* context, + IRType* type) +{ + encodeUInt(context, getTypeID(context, type)); +} + +bool opHasResult(IRValue* inst) { auto type = inst->getType(); - if( !type || type->op != kIROp_VoidType ) + 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 (auto basicType = dynamic_cast(type)) { - return true; + if (basicType->baseType == BaseType::Void) + return false; } - return false; + + return true; } void generateBytecodeForInst( @@ -282,15 +348,16 @@ void generateBytecodeForInst( // auto argCount = inst->getArgCount(); + auto type = inst->getType(); encodeUInt(context, inst->op); + encodeOperand(context, inst->getType()); encodeUInt(context, argCount); for( UInt aa = 0; aa < argCount; ++aa ) { encodeOperand(context, inst->getArg(aa)); } - auto type = inst->getType(); - if( type && type->op == kIROp_VoidType ) + if (!opHasResult(inst)) { // This instructions has no type, so don't emit a destination } @@ -354,6 +421,7 @@ void generateBytecodeForInst( } break; +#if 0 case kIROp_Func: { encodeUInt(context, inst->op); @@ -368,6 +436,7 @@ void generateBytecodeForInst( encodeOperand(context, inst); } break; +#endif case kIROp_Store: { @@ -375,9 +444,9 @@ void generateBytecodeForInst( // We need to encode the type being stored, to make // our lives easier. - encodeOperand(context, inst->getArg(2)->getType()); + encodeOperand(context, inst->getArg(1)->getType()); + encodeOperand(context, inst->getArg(0)); encodeOperand(context, inst->getArg(1)); - encodeOperand(context, inst->getArg(2)); } break; @@ -385,33 +454,166 @@ void generateBytecodeForInst( { encodeUInt(context, inst->op); encodeOperand(context, inst->getType()); - encodeOperand(context, inst->getArg(1)); + encodeOperand(context, inst->getArg(0)); encodeOperand(context, inst); } break; } } -Int getIDForGlobalSymbol( +BytecodeGenerationPtr emitBCType( + BytecodeGenerationContext* context, + Type* type, + IROp op, + BytecodeGenerationPtr const* args, + UInt argCount) +{ + UInt size = sizeof(BCType) + + argCount * sizeof(BCPtr); + + BytecodeGenerationPtr bcAllocation( + context->shared, + allocateRaw(context, size, alignof(BCPtr))); + + BytecodeGenerationPtr bcType = bcAllocation.bitCast(); + auto bcArgs = (bcType + 1).bitCast>(); + + bcType->op = op; + bcType->argCount = 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 = id; + + return bcType; +} + +BytecodeGenerationPtr emitBCVarArgType( + BytecodeGenerationContext* context, + Type* type, + IROp op, + List> args) +{ + return emitBCType(context, type, op, args.Buffer(), args.Count()); +} + +BytecodeGenerationPtr emitBCType( BytecodeGenerationContext* context, - IRInst* inst) + Type* type, + IROp op) +{ + return emitBCType(context, type, op, nullptr, 0); +} + +BytecodeGenerationPtr emitBCType( + BytecodeGenerationContext* context, + Type* type); + +// Emit a `BCType` representation for the given `Type` +BytecodeGenerationPtr emitBCTypeImpl( + BytecodeGenerationContext* context, + Type* type) +{ + // A NULL type is interpreted as equivalent to `Void` for now. + if( !type ) + { + return emitBCType(context, type, kIROp_VoidType); + } + + if( auto basicType = type->As() ) + { + switch(basicType->baseType) + { + case BaseType::Void: return emitBCType(context, type, kIROp_VoidType); + case BaseType::Bool: return emitBCType(context, type, kIROp_BoolType); + case BaseType::Int: return emitBCType(context, type, kIROp_Int32Type); + case BaseType::UInt: return emitBCType(context, type, kIROp_UInt32Type); + case BaseType::UInt64: return emitBCType(context, type, kIROp_UInt64Type); + case BaseType::Half: return emitBCType(context, type, kIROp_Float16Type); + case BaseType::Float: return emitBCType(context, type, kIROp_Float32Type); + case BaseType::Double: return emitBCType(context, type, kIROp_Float64Type); + + default: + break; + } + } + else if( auto funcType = type->As() ) + { + List> operands; + + operands.Add(emitBCType(context, funcType->resultType).bitCast()); + UInt paramCount = funcType->getParamCount(); + for(UInt pp = 0; pp < paramCount; ++pp) + { + operands.Add(emitBCType(context, funcType->getParamType(pp)).bitCast()); + } + + return emitBCVarArgType(context, type, kIROp_FuncType, operands); + } + else if( auto ptrType = type->As() ) + { + List> operands; + operands.Add(emitBCType(context, ptrType->getValueType()).bitCast()); + return emitBCVarArgType(context, type, kIROp_PtrType, operands); + } + else if( auto rwStructuredBufferType = type->As() ) + { + List> operands; + operands.Add(emitBCType(context, rwStructuredBufferType->elementType).bitCast()); + return emitBCVarArgType(context, type, kIROp_readWriteStructuredBufferType, operands); + } + else if( auto structuredBufferType = type->As() ) + { + List> operands; + operands.Add(emitBCType(context, structuredBufferType->elementType).bitCast()); + return emitBCVarArgType(context, type, kIROp_structuredBufferType, operands); + } + + + SLANG_UNEXPECTED("unimplemented"); + return BytecodeGenerationPtr(); +} + +BytecodeGenerationPtr emitBCType( + BytecodeGenerationContext* context, + Type* type) { - Int globalID; - if(context->shared->mapGlobalSymbolToGLobalID.TryGetValue(inst, globalID)) - return globalID; + auto canonical = type->GetCanonicalType(); + UInt id = 0; + if(context->shared->mapTypeToID.TryGetValue(canonical, id)) + { + return context->shared->bcTypes[id]; + } - SLANG_UNEXPECTED("no such ID"); + BytecodeGenerationPtr bcType = emitBCTypeImpl(context, canonical); + return bcType; } -uint32_t getTypeForGlobalSymbol( +uint32_t getTypeID( BytecodeGenerationContext* context, - IRInst* inst) + Type* 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 = emitBCType(context, type); + return bcType->id; +} + +uint32_t getTypeIDForGlobalSymbol( + BytecodeGenerationContext* context, + IRValue* inst) { auto type = inst->getType(); if(!type) return 0; - return getIDForGlobalSymbol(context, type); + return getTypeID(context, type); } BytecodeGenerationPtr allocateString( @@ -442,7 +644,7 @@ BytecodeGenerationPtr allocateString( BytecodeGenerationPtr tryGenerateNameForSymbol( BytecodeGenerationContext* context, - IRInst* inst) + IRGlobalValue* inst) { // TODO: this is gross, and the IR should probably have // a more direct means of querying a name for a symbol. @@ -462,9 +664,10 @@ BytecodeGenerationPtr tryGenerateNameForSymbol( return BytecodeGenerationPtr(); } +// Generate a `BCSymbol` that can represent a global value. BytecodeGenerationPtr generateBytecodeSymbolForInst( BytecodeGenerationContext* context, - IRInst* inst) + IRGlobalValue* inst) { switch( inst->op ) { @@ -474,7 +677,7 @@ BytecodeGenerationPtr generateBytecodeSymbolForInst( BytecodeGenerationPtr bcFunc = allocate(context); bcFunc->op = inst->op; - bcFunc->typeGlobalID = getTypeForGlobalSymbol(context, inst); + bcFunc->typeID = getTypeIDForGlobalSymbol(context, inst); BytecodeGenerationContext subContextStorage; BytecodeGenerationContext* subContext = &subContextStorage; @@ -515,7 +718,17 @@ BytecodeGenerationPtr generateBytecodeSymbolForInst( { UInt blockID = blockCounter++; UInt paramCount = 0; - for( auto ii = bb->firstChild; ii; ii = ii->nextInst ) + + for( auto pp = bb->getFirstParam(); pp; pp = pp->getNextParam() ) + { + // A parameter always uses a register. + regCounter++; + // + // We also want to keep a count of the parameters themselves. + paramCount++; + } + + for( auto ii = bb->getFirstInst(); ii; ii = ii->nextInst ) { switch( ii->op ) { @@ -528,15 +741,6 @@ BytecodeGenerationPtr generateBytecodeSymbolForInst( } 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 @@ -573,30 +777,25 @@ BytecodeGenerationPtr generateBytecodeSymbolForInst( // are always the first N registers in the overall list. // bcBlocks[blockID].params = bcRegs + regCounter; - for( auto ii = bb->firstChild; ii; ii = ii->nextInst ) + for( auto pp = bb->getFirstParam(); pp; pp = pp->getNextParam() ) { - if(ii->op != kIROp_Param) - continue; - Int localID = regCounter++; - subContext->mapInstToLocalID.Add(ii, localID); + subContext->mapInstToLocalID.Add(pp, localID); - bcRegs[localID].op = ii->op; - bcRegs[localID].name = tryGenerateNameForSymbol(context, ii); + bcRegs[localID].op = pp->op; +#if 0 + bcRegs[localID].name = tryGenerateNameForSymbol(context, pp); +#endif bcRegs[localID].previousVarIndexPlusOne = localID; - bcRegs[localID].typeGlobalID = getTypeForGlobalSymbol(context, ii); + bcRegs[localID].typeID = getTypeIDForGlobalSymbol(context, pp); } // Now loop over the non-parameter instructions and // allocate actual register locations to them. - for( auto ii = bb->firstChild; ii; ii = ii->nextInst ) + for( auto ii = bb->getFirstInst(); ii; ii = ii->nextInst ) { switch(ii->op) { - case kIROp_Param: - // Already handled. - break; - default: // For an ordinary instruction with a result, // allocate it here. @@ -606,9 +805,11 @@ BytecodeGenerationPtr generateBytecodeSymbolForInst( subContext->mapInstToLocalID.Add(ii, localID); bcRegs[localID].op = ii->op; +#if 0 bcRegs[localID].name = tryGenerateNameForSymbol(context, ii); +#endif bcRegs[localID].previousVarIndexPlusOne = localID; - bcRegs[localID].typeGlobalID = getTypeForGlobalSymbol(context, ii); + bcRegs[localID].typeID = getTypeIDForGlobalSymbol(context, ii); } break; @@ -624,30 +825,39 @@ BytecodeGenerationPtr generateBytecodeSymbolForInst( subContext->mapInstToLocalID.Add(ii, localID); bcRegs[localID].op = ii->op; +#if 0 bcRegs[localID].name = tryGenerateNameForSymbol(context, ii); +#endif bcRegs[localID].previousVarIndexPlusOne = localID; - bcRegs[localID].typeGlobalID = getTypeForGlobalSymbol(context, ii); + bcRegs[localID].typeID = getTypeIDForGlobalSymbol(context, ii); bcRegs[localID+1].op = ii->op; bcRegs[localID+1].previousVarIndexPlusOne = localID+1; - bcRegs[localID+1].typeGlobalID = getIDForGlobalSymbol(context, - ((IRPtrType*) ii->getType())->getValueType()); + bcRegs[localID+1].typeID = getTypeID(context, + (ii->getType()->As())->getValueType()); } break; } } } + assert(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 blockOffsets; for( auto bb = irFunc->getFirstBlock(); bb; bb = bb->getNextBlock() ) { UInt blockID = blockCounter++; - bcBlocks[blockID].code = getPtr(context); + // Get local bytecode offset for current block. + UInt blockOffset = subContext->currentBytecode.Count(); + blockOffsets.Add( blockOffset ); - for( auto ii = bb->firstChild; ii; ii = ii->nextInst ) + for( auto ii = bb->getFirstInst(); ii; ii = ii->nextInst ) { // What we do with each instruction depends a bit on the // kind of instruction it is. @@ -671,6 +881,22 @@ BytecodeGenerationPtr generateBytecodeSymbolForInst( } } + + // 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 bytes = allocateArray(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. @@ -689,6 +915,17 @@ BytecodeGenerationPtr generateBytecodeSymbolForInst( } break; + case kIROp_global_var: + { + auto bcVar = allocate(context); + + bcVar->op = inst->op; + bcVar->typeID = getTypeID(context, inst->type); + + return bcVar; + } + break; + default: // Most instructions don't need a custom representation. return BytecodeGenerationPtr(); @@ -699,126 +936,98 @@ BytecodeGenerationPtr generateBytecodeForModule( BytecodeGenerationContext* context, IRModule* irModule) { - // The module will get encoded much like a function, - // and then that function will be "invoked" to load - // the module. + // A module in the bytecode is mostly just a list of the + // global symbols in the module. // - auto bcModule = allocate(context); - bcModule->op = irModule->op; - bcModule->typeGlobalID = 0; - - // The logical function that the module representats - // will only have a single block, so we can allocate it now. + // 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. // - auto bcBlock = allocate(context); - bcBlock->paramCount = 0; - bcBlock->params = BytecodeGenerationPtr(); - - bcModule->blockCount = 1; - bcModule->blocks = bcBlock; - - // Because the module is the top-most level, there is - // no need for it to have "constants" that represent - // values imported from the next outer scope. + // 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. // - bcModule->constCount = 0; - bcModule->consts = BytecodeGenerationPtr(); + auto bcModule = allocate(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 regCount = 0; - for( auto inst = irModule->firstChild; inst; inst = inst->nextInst ) + UInt symbolCount = 0; + for( auto gv : irModule->globalValues ) { - if(!opHasResult(inst)) - continue; - - Int globalID = Int(regCount++); + Int globalID = Int(symbolCount++); - context->shared->mapGlobalSymbolToGLobalID.Add(inst, globalID); + // Ensure that local code inside functions can see these symbols + BCConst bcConst; + bcConst.flavor = kBCConstFlavor_GlobalSymbol; + bcConst.id = globalID; + context->shared->mapValueToGlobal.Add(gv, bcConst); // In the global scope, global IDs are also the local IDs - context->mapInstToLocalID.Add(inst, globalID); + context->mapInstToLocalID.Add(gv, globalID); } - auto bcRegs = allocateArray(context, regCount); + auto bcSymbols = allocateArray>(context, symbolCount); - bcModule->regCount = regCount; - bcModule->regs = bcRegs; + bcModule->symbolCount = symbolCount; + bcModule->symbols = bcSymbols; - // Now lets walk through and initialize all those bytecode - // register representations so that we can use them. - UInt regCounter= 0; - for( auto inst = irModule->firstChild; inst; inst = inst->nextInst ) + for( auto gv : irModule->globalValues ) { - if(!opHasResult(inst)) - continue; - - UInt regIndex = *context->mapInstToLocalID.TryGetValue(inst); + UInt symbolIndex = *context->mapInstToLocalID.TryGetValue(gv); - BytecodeGenerationPtr name = tryGenerateNameForSymbol(context, inst); - - bcRegs[regIndex].op = inst->op; - bcRegs[regIndex].name = name; - bcRegs[regIndex].typeGlobalID = getTypeForGlobalSymbol(context, inst); - bcRegs[regIndex].previousVarIndexPlusOne = regIndex; - } - - // Some instructions represent "nested" symbols that will need - // custom handling, and we will represent those here. - List> nestedSymbols; - for( auto inst = irModule->firstChild; inst; inst = inst->nextInst ) - { - UInt regIndex = *context->mapInstToLocalID.TryGetValue(inst); - - auto bcSymbol = generateBytecodeSymbolForInst(context, inst); + auto bcSymbol = generateBytecodeSymbolForInst(context, gv); if (!bcSymbol.getPtr()) continue; - UInt nestedSymbolID = nestedSymbols.Count(); - nestedSymbols.Add(bcSymbol); - - context->mapInstToNestedID.Add(inst, nestedSymbolID); + auto name = tryGenerateNameForSymbol(context, gv); + bcSymbol->name = name; - bcSymbol->name = bcRegs[regIndex].name; + bcSymbols[symbolIndex] = bcSymbol; } - auto nestedSymbolCount = nestedSymbols.Count(); - auto bcNestedSymbols = allocateArray>(context, nestedSymbolCount); + // At this point we should have identified all the literals we need: + UInt constantCount = context->shared->constants.Count(); + auto bcConstants = allocateArray(context, constantCount); + bcModule->constantCount = constantCount; + bcModule->constants = bcConstants; - bcModule->nestedSymbolCount = nestedSymbolCount; - bcModule->nestedSymbols = bcNestedSymbols; - for (UInt ii = 0; ii < nestedSymbolCount; ++ii) + for(UInt cc = 0; cc < constantCount; ++cc) { - bcNestedSymbols[ii] = nestedSymbols[ii]; - } - + auto irConstant = (IRConstant*) context->shared->constants[cc]; + bcConstants[cc].op = irConstant->op; + bcConstants[cc].typeID = getTypeID(context, irConstant->type); - // Finally, we can go through and emit the actual code for - // the initialization step. - bcBlock->code = getPtr(context); - for( auto inst = irModule->firstChild; inst; inst = inst->nextInst ) - { - // Generate bytecode for global-scope inst - generateBytecodeForInst(context, inst); - } - // Need to encode a terminator here, just to keep the encoding valid - encodeUInt(context, kIROp_ReturnVoid); + switch(irConstant->op) + { + case kIROp_IntLit: + { + auto ptr = allocate(context); + *ptr = irConstant->u.intVal; + bcConstants[cc].ptr = ptr.bitCast(); + } + break; -#if 0 + default: + break; + } - // Now we can go through and generate the bytecode object - // that will represent each of these global symbols + } - List> globalSymbols; + // At this point we should have collected all the types we need: + UInt typeCount = context->shared->bcTypes.Count(); + auto bcTypes = allocateArray>(context, typeCount); + bcModule->typeCount = typeCount; + bcModule->types = bcTypes; - for( auto inst = irModule->firstChild; inst; inst = inst->nextInst ) + for(UInt tt = 0; tt < typeCount; ++tt) { - // Generate bytecode for global-scope inst - auto globalSymbol = generateBytecodeForGlobalSymbol(context, inst); - globalSymbols.Add(globalSymbol); + bcTypes[tt] = context->shared->bcTypes[tt]; } -#endif + return bcModule; } @@ -833,10 +1042,6 @@ void generateBytecodeStream( memcpy(header->magic, "slang\0bc", sizeof(header->magic)); header->version = 0; - // HACK: ensure that a NULL pointer in an operand field can - // be encoded. - context->shared->mapGlobalSymbolToGLobalID.Add(nullptr, -1); - header->module = generateBytecodeForModule(context, irModule); } -- cgit v1.2.3