summaryrefslogtreecommitdiff
path: root/source/slang/slang-emit-spirv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-emit-spirv.cpp')
-rw-r--r--source/slang/slang-emit-spirv.cpp171
1 files changed, 140 insertions, 31 deletions
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index d93a50756..a3051fadb 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -291,6 +291,41 @@ struct SpvLiteralBits
{
static SpvLiteralBits from32(uint32_t value) { return SpvLiteralBits{{value}}; }
static SpvLiteralBits from64(uint64_t value) { return SpvLiteralBits{{SpvWord(value), SpvWord(value >> 32)}}; }
+ static SpvLiteralBits fromUnownedStringSlice(UnownedStringSlice text)
+ {
+ SpvLiteralBits result;
+
+ // [Section 2.2.1 : Instructions]
+ //
+ // > Literal String: A nul-terminated stream of characters consuming
+ // > an integral number of words. The character set is Unicode in the
+ // > UTF-8 encoding scheme. The UTF-8 octets (8-bit bytes) are packed
+ // > four per word, following the little-endian convention (i.e., the
+ // > first octet is in the lowest-order 8 bits of the word).
+ // > The final word contains the string’s nul-termination character (0), and
+ // > all contents past the end of the string in the final word are padded with 0.
+
+ // First work out the amount of words we'll need
+ const Index textCount = text.getLength();
+ // Calculate the minimum amount of bytes needed - which needs to include terminating 0
+ const Index minByteCount = textCount + 1;
+ // Calculate the amount of words including padding if necessary
+ const Index wordCount = (minByteCount + 3) >> 2;
+
+ // Make space on the operand stack, keeping the free space start in operandStartIndex
+ result.value.setCount(wordCount);
+
+ // Set dst to the start of the operand memory
+ char* dst = (char*)(result.value.getBuffer());
+
+ // Copy the text
+ memcpy(dst, text.begin(), textCount);
+
+ // Set terminating 0, and remaining buffer 0s
+ memset(dst + textCount, 0, wordCount * sizeof(SpvWord) - textCount);
+
+ return result;
+ }
List<SpvWord> value; // Words, stored low words to high (TODO, SmallArray or something here)
};
@@ -482,6 +517,9 @@ struct SPIRVEmitContext
// `IRInst` may not have been emitted.
Dictionary<IRInst*, SpvWord> m_mapIRInstToSpvID;
+ // Map a Slang IR instruction to the corresponding SPIR-V debug instruction.
+ Dictionary<IRInst*, SpvInst*> m_mapIRInstToSpvDebugInst;
+
/// Register that `irInst` maps to `spvInst`
void registerInst(IRInst* irInst, SpvInst* spvInst)
{
@@ -498,6 +536,23 @@ struct SPIRVEmitContext
}
}
+ /// Register that `irInst` has debug info represented by `spvDebugInst`.
+ void registerDebugInst(IRInst* irInst, SpvInst* spvDebugInst)
+ {
+ m_mapIRInstToSpvDebugInst.add(irInst, spvDebugInst);
+ }
+
+ SpvInst* findDebugScope(IRInst* inst)
+ {
+ for (auto parent = inst; parent; parent = parent->getParent())
+ {
+ SpvInst* spvInst = nullptr;
+ if (m_mapIRInstToSpvDebugInst.tryGetValue(parent, spvInst))
+ return spvInst;
+ }
+ return nullptr;
+ }
+
/// Get or reserve a SpvID for an IR value.
SpvWord getIRInstSpvID(IRInst* inst)
{
@@ -728,36 +783,7 @@ struct SPIRVEmitContext
// Assert that `text` doesn't contain any embedded nul bytes, since they
// could lead to invalid encoded results.
SLANG_ASSERT(text.indexOf(0) < 0);
-
- // [Section 2.2.1 : Instructions]
- //
- // > Literal String: A nul-terminated stream of characters consuming
- // > an integral number of words. The character set is Unicode in the
- // > UTF-8 encoding scheme. The UTF-8 octets (8-bit bytes) are packed
- // > four per word, following the little-endian convention (i.e., the
- // > first octet is in the lowest-order 8 bits of the word).
- // > The final word contains the string’s nul-termination character (0), and
- // > all contents past the end of the string in the final word are padded with 0.
-
- // First work out the amount of words we'll need
- const Index textCount = text.getLength();
- // Calculate the minimum amount of bytes needed - which needs to include terminating 0
- const Index minByteCount = textCount + 1;
- // Calculate the amount of words including padding if necessary
- const Index wordCount = (minByteCount + 3) >> 2;
-
- // Make space on the operand stack, keeping the free space start in operandStartIndex
- const Index operandStartIndex = m_operandStack.getCount();
- m_operandStack.setCount(operandStartIndex + wordCount);
-
- // Set dst to the start of the operand memory
- char* dst = (char*)(m_operandStack.getBuffer() + operandStartIndex);
-
- // Copy the text
- memcpy(dst, text.begin(), textCount);
-
- // Set terminating 0, and remaining buffer 0s
- memset(dst + textCount, 0, wordCount * sizeof(SpvWord) - textCount);
+ emitOperand(SpvLiteralBits::fromUnownedStringSlice(text));
}
// Sometimes we will want to pass down an argument that
@@ -1014,6 +1040,7 @@ struct SPIRVEmitContext
# define SLANG_IN_SPIRV_EMIT_CONTEXT
# include "slang-emit-spirv-ops.h"
+ #include "slang-emit-spirv-ops-debug-info-ext.h"
# undef SLANG_IN_SPIRV_EMIT_CONTEXT
/// The SPIRV OpExtInstImport inst that represents the GLSL450
@@ -1031,6 +1058,21 @@ struct SPIRVEmitContext
return m_glsl450ExtInst;
}
+ /// The SPIRV OpExtInstImport inst that represents the NonSemantic debug info
+ /// extended instruction set.
+ SpvInst* m_NonSemanticDebugInfoExtInst = nullptr;
+
+ SpvInst* getNonSemanticDebugInfoExtInst()
+ {
+ if (m_NonSemanticDebugInfoExtInst)
+ return m_NonSemanticDebugInfoExtInst;
+ m_NonSemanticDebugInfoExtInst = emitOpExtInstImport(
+ getSection(SpvLogicalSectionID::ExtIntInstImports),
+ nullptr,
+ UnownedStringSlice("NonSemantic.Shader.DebugInfo.100"));
+ return m_NonSemanticDebugInfoExtInst;
+ }
+
// Now that we've gotten the core infrastructure out of the way,
// let's start looking at emitting some instructions that make
// up a SPIR-V module.
@@ -1152,7 +1194,6 @@ struct SPIRVEmitContext
const FloatInfo i = getFloatingTypeInfo(as<IRType>(inst));
return emitOpTypeFloat(inst, SpvLiteralInteger::from32(int32_t(i.width)));
}
-
case kIROp_PtrType:
case kIROp_RefType:
case kIROp_OutType:
@@ -1342,6 +1383,7 @@ struct SPIRVEmitContext
case kIROp_BoolLit:
case kIROp_IntLit:
case kIROp_FloatLit:
+ case kIROp_StringLit:
return emitLit(inst);
case kIROp_GlobalParam:
@@ -1359,6 +1401,36 @@ struct SPIRVEmitContext
dumpIRToString(g);
SLANG_UNEXPECTED(e.getBuffer());
}
+
+ case kIROp_DebugSource:
+ {
+ ensureExtensionDeclaration(UnownedStringSlice("SPV_KHR_non_semantic_info"));
+ auto debugSource = as<IRDebugSource>(inst);
+ auto result = emitOpDebugSource(
+ getSection(SpvLogicalSectionID::ConstantsAndTypes),
+ inst,
+ inst->getFullType(),
+ getNonSemanticDebugInfoExtInst(),
+ debugSource->getFileName(),
+ debugSource->getSource());
+ auto moduleInst = inst->getModule()->getModuleInst();
+ if (!m_mapIRInstToSpvDebugInst.containsKey(moduleInst))
+ {
+ IRBuilder builder(inst);
+ builder.setInsertBefore(inst);
+ auto translationUnit = emitOpDebugCompilationUnit(
+ getSection(SpvLogicalSectionID::ConstantsAndTypes),
+ moduleInst,
+ inst->getFullType(),
+ getNonSemanticDebugInfoExtInst(),
+ emitIntConstant(100, builder.getUIntType()), // ExtDebugInfo version.
+ emitIntConstant(5, builder.getUIntType()), // DWARF version.
+ result,
+ emitIntConstant(6, builder.getUIntType())); // Language, use HLSL's ID for now.
+ registerDebugInst(moduleInst, translationUnit);
+ }
+ return result;
+ }
default:
{
String e = "Unhandled global inst in spirv-emit:\n"
@@ -1909,6 +1981,9 @@ struct SPIRVEmitContext
}
case kIROp_MakeArray:
return emitConstruct(parent, inst);
+
+ case kIROp_DebugLine:
+ return emitDebugLine(parent, as<IRDebugLine>(inst));
}
}
@@ -1944,6 +2019,11 @@ struct SPIRVEmitContext
);
}
}
+ case kIROp_StringLit:
+ {
+ auto value = as<IRStringLit>(inst)->getStringSlice();
+ return emitInst(getSection(SpvLogicalSectionID::DebugStringsAndSource), inst, SpvOpString, kResultID, SpvLiteralBits::fromUnownedStringSlice(value));
+ }
default:
return nullptr;
}
@@ -2085,6 +2165,17 @@ struct SPIRVEmitContext
name,
params
);
+
+ // Stage specific execution mode declarations.
+ switch (entryPointDecor->getProfile().getStage())
+ {
+ case Stage::Fragment:
+ //OpExecutionMode %main OriginUpperLeft
+ emitInst(getSection(SpvLogicalSectionID::ExecutionModes), nullptr, SpvOpExecutionMode, dstID, SpvExecutionModeOriginUpperLeft);
+ break;
+ default:
+ break;
+ }
}
break;
@@ -3138,6 +3229,19 @@ struct SPIRVEmitContext
SLANG_UNREACHABLE("Arithmetic op with 0 or more than 2 operands");
}
+ SpvInst* emitDebugLine(SpvInstParent* parent, IRDebugLine* debugLine)
+ {
+ auto scope = findDebugScope(debugLine);
+ if (!scope)
+ return nullptr;
+ return emitOpDebugLine(parent, debugLine, debugLine->getFullType(), getNonSemanticDebugInfoExtInst(),
+ debugLine->getSource(),
+ debugLine->getLineStart(),
+ debugLine->getLineEnd(),
+ debugLine->getColStart(),
+ debugLine->getColEnd());
+ }
+
OrderedHashSet<SpvCapability> m_capabilities;
void requireSPIRVCapability(SpvCapability capability)
@@ -3212,6 +3316,11 @@ SlangResult emitSPIRVFromIR(
#endif
context.emitFrontMatter();
+ for (auto inst : irModule->getGlobalInsts())
+ {
+ if (as<IRDebugSource>(inst))
+ context.ensureInst(inst);
+ }
for (auto irEntryPoint : irEntryPoints)
{
context.ensureInst(irEntryPoint);