diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/slang-memory-arena.cpp | 35 | ||||
| -rw-r--r-- | source/core/slang-token-reader.h | 9 | ||||
| -rw-r--r-- | source/slang/slang-compiler.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 213 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 13 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 7 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 88 |
7 files changed, 304 insertions, 66 deletions
diff --git a/source/core/slang-memory-arena.cpp b/source/core/slang-memory-arena.cpp index 99e6261ff..a9fc1ec19 100644 --- a/source/core/slang-memory-arena.cpp +++ b/source/core/slang-memory-arena.cpp @@ -42,19 +42,19 @@ void MemoryArena::_initialize(size_t blockPayloadSize, size_t alignment) // Ensure it's alignment is at least kMinAlignment alignment = (alignment < kMinAlignment) ? kMinAlignment : alignment; + const size_t alignMask = alignment - 1; + + // Make sure the payload is rounded up to the alignment + blockPayloadSize = (blockPayloadSize + alignMask) & ~alignMask; + m_blockPayloadSize = blockPayloadSize; - size_t blockAllocSize = blockPayloadSize; // If alignment required is larger then the backing allocators then // make larger to ensure when alignment correction takes place it will be aligned - if (alignment > kMinAlignment) - { - blockAllocSize += alignment; - } - + const size_t blockAllocSize = (alignment > kMinAlignment) ? (blockPayloadSize + alignment) : blockPayloadSize; + m_blockAllocSize = blockAllocSize; m_blockAlignment = alignment; - m_availableBlocks = nullptr; m_blockFreeList.init(sizeof(Block), sizeof(void*), 16); @@ -217,12 +217,17 @@ MemoryArena::Block* MemoryArena::_newNormalBlock() return block; } - return _newBlock(m_blockAllocSize, m_blockAlignment); + Block* block = _newBlock(m_blockAllocSize, m_blockAlignment); + // Check that every normal block has m_blockPayloadSize space + assert(size_t(block->m_end - block->m_start) >= m_blockPayloadSize); + return block; } MemoryArena::Block* MemoryArena::_newBlock(size_t allocSize, size_t alignment) { assert(alignment >= m_blockAlignment); + // Alignment must be a power of 2 + assert(((alignment - 1) & alignment) == 0); // Allocate block Block* block = (Block*)m_blockFreeList.allocate(); @@ -289,9 +294,10 @@ void* MemoryArena::_allocateAlignedFromNewBlock(size_t size, size_t alignment) // // An improvement might be to have some abstraction that sits on top that can do this tracking (or have the blocks // themselves record if they alias over a previously used block - but we don't bother with this here. - if (allocSize > m_blockAllocSize) + // If the alignment is greater than regular alignment we need to handle specially + if (allocSize > m_blockPayloadSize || (alignment > m_blockAlignment && allocSize + alignment > m_blockPayloadSize)) { - // This is an odd-sized block so just allocate the whole thing + // This is an odd-sized block so just allocate the whole thing. block = _newBlock(allocSize, alignment); } else @@ -310,10 +316,11 @@ void* MemoryArena::_allocateAlignedFromNewBlock(size_t size, size_t alignment) // Make the current block _addCurrentBlock(block); - // Allocated memory is just the start of this block - uint8_t* memory = m_current; - // It must already be aligned - assert((size_t(m_current) & alignMask) == 0); + // Align the memory + uint8_t* memory = (uint8_t*)((size_t(m_current) + alignMask) & ~alignMask); + + // It must be aligned + assert((size_t(memory) & alignMask) == 0); // Do the aligned allocation (which must fit) by aligning the pointer // It must fit if the previous code is correct... diff --git a/source/core/slang-token-reader.h b/source/core/slang-token-reader.h index f8a455452..474db5c4a 100644 --- a/source/core/slang-token-reader.h +++ b/source/core/slang-token-reader.h @@ -189,6 +189,15 @@ namespace Slang { tokenPtr -= count; } + Token ReadMatchingToken(TokenType type) + { + auto token = ReadToken(); + if (token.Type != type) + { + throw TextFormatException("Text parsing error: unexpected token."); + } + return token; + } Token ReadToken() { if (tokenPtr < (int)tokens.getCount()) diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index 07e2b66fe..2d5557371 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -1529,6 +1529,11 @@ SlangResult dissassembleDXILUsingDXC( sharedLib->m_temporaryFileSet = productFileSet; productFileSet.clear(); + // Copy the paths in the temporary file set + // We particularly want to do this to keep the source + sharedLib->m_temporaryFileSet.add(temporaryFileSet.m_paths); + temporaryFileSet.clear(); + // Output the shared library outSharedLib = sharedLib; } diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index dc9ab23cc..194ebfa2c 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -385,21 +385,7 @@ void CPPSourceEmitter::emitTypeDefinition(IRType* inType) } UnownedStringSlice CPPSourceEmitter::_getTypeName(IRType* inType) -{ - if (isNominalOp(inType->op)) - { - StringSlicePool::Handle handle; - // NOTE! This is somewhat unusual -> we are going to add types which aren't cloned and belong to - // m_uniqueModule. We *assume* nominal types are de-duped - if (!m_typeNameMap.TryGetValue(inType, handle)) - { - auto name = getName(inType); - handle = m_slicePool.add(name); - m_typeNameMap.Add(inType, handle); - } - return m_slicePool.getSlice(handle); - } - +{ IRType* type = _cloneType(inType); StringSlicePool::Handle handle = StringSlicePool::kNullHandle; @@ -480,6 +466,26 @@ SlangResult CPPSourceEmitter::_calcTextureTypeName(IRTextureTypeBase* texType, S return SLANG_OK; } +static UnownedStringSlice _getResourceTypePrefix(IROp op) +{ + switch (op) + { + case kIROp_HLSLStructuredBufferType: return UnownedStringSlice::fromLiteral("StructuredBuffer"); + case kIROp_HLSLRWStructuredBufferType: return UnownedStringSlice::fromLiteral("RWStructuredBuffer"); + case kIROp_HLSLRWByteAddressBufferType: return UnownedStringSlice::fromLiteral("RWByteAddressBuffer"); + case kIROp_HLSLByteAddressBufferType: return UnownedStringSlice::fromLiteral("ByteAddressBuffer"); + case kIROp_SamplerStateType: return UnownedStringSlice::fromLiteral("SamplerState"); + case kIROp_SamplerComparisonStateType: return UnownedStringSlice::fromLiteral("SamplerComparisonState"); + case kIROp_HLSLRasterizerOrderedStructuredBufferType: return UnownedStringSlice::fromLiteral("RasterizerOrderedStructuredBuffer"); + case kIROp_HLSLAppendStructuredBufferType: return UnownedStringSlice::fromLiteral("AppendStructuredBuffer"); + case kIROp_HLSLConsumeStructuredBufferType: return UnownedStringSlice::fromLiteral("ConsumeStructuredBuffer"); + case kIROp_HLSLRasterizerOrderedByteAddressBufferType: return UnownedStringSlice::fromLiteral("RasterizerOrderedByteAddressBuffer"); + case kIROp_RaytracingAccelerationStructureType: return UnownedStringSlice::fromLiteral("RaytracingAccelerationStructure"); + + default: return UnownedStringSlice(); + } +} + SlangResult CPPSourceEmitter::_calcTypeName(IRType* type, CodeGenTarget target, StringBuilder& out) { switch (type->op) @@ -540,15 +546,6 @@ SlangResult CPPSourceEmitter::_calcTypeName(IRType* type, CodeGenTarget target, } return SLANG_OK; } - case kIROp_HLSLRWStructuredBufferType: - { - auto bufType = static_cast<IRHLSLRWStructuredBufferType*>(type); - - out << "RWStructuredBuffer<"; - SLANG_RETURN_ON_FAIL(_calcTypeName(bufType->getElementType(), target, out)); - out << ">"; - return SLANG_OK; - } case kIROp_ArrayType: { auto arrayType = static_cast<IRArrayType*>(type); @@ -560,18 +557,14 @@ SlangResult CPPSourceEmitter::_calcTypeName(IRType* type, CodeGenTarget target, out << ", " << elementCount << ">"; return SLANG_OK; } - case kIROp_SamplerStateType: - { - out << "SamplerState"; - return SLANG_OK; - } - case kIROp_SamplerComparisonStateType: - { - out << "SamplerComparisonState"; - return SLANG_OK; - } default: { + if (isNominalOp(type->op)) + { + out << getName(type); + return SLANG_OK; + } + if (IRBasicType::isaImpl(type->op)) { out << getBuiltinTypeName(type->op); @@ -587,6 +580,47 @@ SlangResult CPPSourceEmitter::_calcTypeName(IRType* type, CodeGenTarget target, } } + // If _getResourceTypePrefix returns something, we assume can output any specialization after it in order. + { + UnownedStringSlice prefix = _getResourceTypePrefix(type->op); + if (prefix.size() > 0) + { + auto oldWriter = m_writer; + SourceManager* sourceManager = oldWriter->getSourceManager(); + + // TODO(JS): This is a bit of a hack. We don't want to emit the result here, + // so we replace the writer, write out the type, grab the contents, and restore the writer + + SourceWriter writer(sourceManager, LineDirectiveMode::None); + m_writer = &writer; + + m_writer->emit(prefix); + + // TODO(JS). + // Assumes ordering of types matches ordering of operands. + + UInt operandCount = type->getOperandCount(); + if (operandCount) + { + m_writer->emit("<"); + for (UInt ii = 0; ii < operandCount; ++ii) + { + if (ii != 0) + { + m_writer->emit(", "); + } + emitVal(type->getOperand(ii), getInfo(EmitOp::General)); + } + m_writer->emit(">"); + } + + out << writer.getContent(); + + m_writer = oldWriter; + return SLANG_OK; + } + } + break; } } @@ -607,29 +641,42 @@ IRInst* CPPSourceEmitter::_clone(IRInst* inst) return nullptr; } + IRModule* module = inst->getModule(); + // All inst's must belong to a module + SLANG_ASSERT(module); + // If it's in this module then we don't need to clone - if (inst->getModule() == m_uniqueModule) + if (module == m_uniqueModule) { return inst; } - if (isNominalOp(inst->op)) - { - // If it's nominal we don't bother copying, as we assumed it is already de-duped - return inst; - } - if (IRInst*const* newInstPtr = m_cloneMap.TryGetValue(inst)) { return *newInstPtr; } - + + if (isNominalOp(inst->op)) + { + // TODO(JS) + // This is arguably problematic - I'm adding an instruction from another module to the map, to be it's self. + // I did have code which created a copy of the nominal instruction and name hint, but because nominality means + // 'same address' other code would generate a different name for that instruction (say as compared to being a member in + // the original instruction) + // + // Because I use findOrAddInst which doesn't hoist instructions, the hoisting doesn't rely on parenting, that would + // break. + + // If nominal, we just use the original inst + m_cloneMap.Add(inst, inst); + return inst; + } + // It would be nice if I could use ir-clone.cpp to do this -> but it doesn't clone // operands. We wouldn't want to clone decorations, and it can't clone IRConstant(!) so // it's no use IRInst* clone = nullptr; - switch (inst->op) { case kIROp_IntLit: @@ -639,6 +686,12 @@ IRInst* CPPSourceEmitter::_clone(IRInst* inst) clone = m_irBuilder.getIntValue(cloneType, intLit->value.intVal); break; } + case kIROp_StringLit: + { + auto stringLit = static_cast<IRStringLit*>(inst); + clone = m_irBuilder.getStringValue(stringLit->getStringSlice()); + break; + } default: { if (IRBasicType::isaImpl(inst->op)) @@ -661,7 +714,12 @@ IRInst* CPPSourceEmitter::_clone(IRInst* inst) cloneOperands[i] = _clone(inst->getOperand(i)); } - clone = m_irBuilder.findOrEmitHoistableInst(cloneType, inst->op, operandCount, cloneOperands.getBuffer()); + //clone = m_irBuilder.findOrEmitHoistableInst(cloneType, inst->op, operandCount, cloneOperands.getBuffer()); + + UInt operandCounts[1] = { UInt(operandCount) }; + IRInst*const* listOperands[1] = { cloneOperands.getBuffer() }; + + clone = m_irBuilder.findOrAddInst(cloneType, inst->op, 1, operandCounts, listOperands); } else { @@ -1088,6 +1146,42 @@ void CPPSourceEmitter::_emitLengthDefinition(const UnownedStringSlice& funcName, writer->emit("}\n\n"); } +void CPPSourceEmitter::_emitGetAtDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp) +{ + SourceWriter* writer = getSourceWriter(); + + IRFuncType* funcType = specOp.signatureType; + SLANG_ASSERT(funcType->getParamCount() == 2); + + IRType* srcType = funcType->getParamType(0); + + IRType* retType = specOp.returnType; + emitType(retType); + m_writer->emit("& "); + + writer->emit(funcName); + writer->emit("("); + + emitType(funcType->getParamType(0)); + writer->emit("& a, "); + emitType(funcType->getParamType(1)); + writer->emit(" b)\n{\n"); + + writer->indent(); + + IRVectorType* vectorType = as<IRVectorType>(srcType); + int vecSize = int(GetIntVal(vectorType->getElementCount())); + + writer->emit("assert(b >= 0 && b < "); + writer->emit(vecSize); + writer->emit(");\n"); + + writer->emit("return (&a.x)[b];\n"); + + writer->dedent(); + writer->emit("}\n\n"); +} + void CPPSourceEmitter::_emitNormalizeDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp) { SourceWriter* writer = getSourceWriter(); @@ -1332,10 +1426,16 @@ void CPPSourceEmitter::emitSpecializedOperationDefinition(const SpecializedIntri { return _emitConstructFromScalarDefinition(_getFuncName(specOp), specOp); } + case IntrinsicOp::GetAt: + { + return _emitGetAtDefinition(_getFuncName(specOp), specOp); + } default: { const auto& info = getOperationInfo(specOp.op); - if (info.numOperands >= 1 && info.numOperands <= 3) + const int paramCount = (info.numOperands < 0) ? int(specOp.signatureType->getParamCount()) : info.numOperands; + + if (paramCount >= 1 && paramCount <= 3) { return _emitAryDefinition(specOp); } @@ -1500,7 +1600,7 @@ void CPPSourceEmitter::emitCall(const SpecializedIntrinsic& specOp, IRInst* inst useType(specOp.returnType); // add that we want a function - SLANG_ASSERT(numOperands == info.numOperands); + SLANG_ASSERT(info.numOperands < 0 || numOperands == info.numOperands); if (isOperator) { @@ -1603,6 +1703,14 @@ StringSlicePool::Handle CPPSourceEmitter::_calcFuncName(const SpecializedIntrins } return m_slicePool.add(builder); } + case IntrinsicOp::GetAt: + { + return m_slicePool.add(UnownedStringSlice::fromLiteral("getAt")); + } + case IntrinsicOp::SetAt: + { + return m_slicePool.add(UnownedStringSlice::fromLiteral("setAt")); + } default: break; } @@ -2193,7 +2301,18 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut return false; } - + case kIROp_getElement: + case kIROp_getElementPtr: + { + IRInst* target = inst->getOperand(0); + if (target->getDataType()->op == kIROp_VectorType) + { + // Specially handle this + emitOperationCall(IntrinsicOp::GetAt, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec); + return true; + } + return false; + } default: { IntrinsicOp op = getOperation(inst->op); diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h index 4280bdc80..6c300320a 100644 --- a/source/slang/slang-emit-cpp.h +++ b/source/slang/slang-emit-cpp.h @@ -110,11 +110,15 @@ just constructXXXFromScalar. Would be good if there was a suitable name to encom x(Step, "step", 2) \ \ x(AsFloat, "asfloat", 1) \ - x(AsInt, "asint", 1) \ - x(AsUInt, "asuint", 1) \ + x(AsInt, "asint", -1) \ + x(AsUInt, "asuint", -1) \ + x(AsDouble, "asdouble", 2) \ \ x(ConstructConvert, "", 1) \ - x(ConstructFromScalar, "", 1) + x(ConstructFromScalar, "", 1) \ + \ + x(GetAt, "", 2) \ + x(SetAt, "", 3) class CPPSourceEmitter: public CLikeSourceEmitter @@ -224,7 +228,8 @@ protected: void _emitReflectDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); void _emitConstructConvertDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); void _emitConstructFromScalarDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); - + void _emitGetAtDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); + void _emitSignature(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); void _emitInOutParamType(IRType* type, String const& name, IRType* valueType); diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index cd59630d8..2e2168b1a 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -955,6 +955,13 @@ struct IRBuilder IRUndefined* emitUndefined(IRType* type); + IRInst* findOrAddInst( + IRType* type, + IROp op, + UInt operandListCount, + UInt const* listOperandCounts, + IRInst* const* const* listOperands); + IRInst* findOrEmitHoistableInst( IRType* type, IROp op, diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 2e8de5e37..b6c88bb99 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -1528,7 +1528,6 @@ namespace Slang keyInst.value.ptrVal = value; return (IRPtrLit*) findOrEmitConstant(this, keyInst); } - IRInst* IRBuilder::findOrEmitHoistableInst( IRType* type, @@ -1617,6 +1616,93 @@ namespace Slang return inst; } + IRInst* IRBuilder::findOrAddInst( + IRType* type, + IROp op, + UInt operandListCount, + UInt const* listOperandCounts, + IRInst* const* const* listOperands) + { + UInt operandCount = 0; + for (UInt ii = 0; ii < operandListCount; ++ii) + { + operandCount += listOperandCounts[ii]; + } + + auto& memoryArena = getModule()->memoryArena; + void* cursor = memoryArena.getCursor(); + + // We are going to create a 'dummy' instruction on the memoryArena + // which can be used as a key for lookup, so see if we + // already have an equivalent instruction available to use. + size_t keySize = sizeof(IRInst) + operandCount * sizeof(IRUse); + IRInst* inst = (IRInst*)memoryArena.allocateAndZero(keySize); + + void* endCursor = memoryArena.getCursor(); + // Mark as 'unused' cos it is unused on release builds. + SLANG_UNUSED(endCursor); + + new(inst) IRInst(); + inst->op = op; + inst->typeUse.usedValue = type; + inst->operandCount = (uint32_t)operandCount; + + // Don't link up as we may free (if we already have this key) + { + IRUse* operand = inst->getOperands(); + for (UInt ii = 0; ii < operandListCount; ++ii) + { + UInt listOperandCount = listOperandCounts[ii]; + for (UInt jj = 0; jj < listOperandCount; ++jj) + { + operand->usedValue = listOperands[ii][jj]; + operand++; + } + } + } + + // Find or add the key/inst + { + IRInstKey key = { inst }; + + // Ideally we would add if not found, else return if was found instead of testing & then adding. + IRInst** found = sharedBuilder->globalValueNumberingMap.TryGetValueOrAdd(key, inst); + SLANG_ASSERT(endCursor == memoryArena.getCursor()); + // If it's found, just return, and throw away the instruction + if (found) + { + memoryArena.rewindToCursor(cursor); + return *found; + } + } + + // Make the lookup 'inst' instruction into 'proper' instruction. Equivalent to + // IRInst* inst = createInstImpl<IRInst>(builder, op, type, 0, nullptr, operandListCount, listOperandCounts, listOperands); + { + if (type) + { + inst->typeUse.usedValue = nullptr; + inst->typeUse.init(inst, type); + } + + maybeSetSourceLoc(this, inst); + + IRUse*const operands = inst->getOperands(); + for (UInt i = 0; i < operandCount; ++i) + { + IRUse& operand = operands[i]; + auto value = operand.usedValue; + + operand.usedValue = nullptr; + operand.init(inst, value); + } + } + + addInst(inst); + return inst; + } + + IRInst* IRBuilder::findOrEmitHoistableInst( IRType* type, IROp op, |
