summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-02-04 18:45:50 -0500
committerGitHub <noreply@github.com>2021-02-04 15:45:50 -0800
commitfb053433ef64bbae50a8a10ea4381a5695019fac (patch)
tree87298897bf88ec79b40343a868a283d1885357b9
parentc40f10b704b8bd5a744cc9b3964344585436b1ac (diff)
Fix line offset problem (#1690)
* #include an absolute path didn't work - because paths were taken to always be relative. * WIP diagnostics for line number output. * Small param naming change * Use x macro for pass through compile human name lookup/getting. * WIP on parsing downstream compiler output. * Split out parsing into ParseDiagnosticUtil. Added test result of single line. * Dump out the std output on fail to parse diagnostics. * Change test type for syntax-error-intrinsic.slang be TEST not TEST_DIAGNOSTIC * Use Index for StringUtil. * WIP: First pass support for parsing Slang diagnostics. * WIP Testing comparing with ParseDiagnosticUtil with previous ad-hoc mechanism. * Use the new parsing mechanism for diagnostic comparisons. * Fix layout on GLSL, doesn't have CR so runs into main. * Split out switch on outputting intrinsic 'specials'. Output code around intrinsic as emit - so that we get the appropriate indenting (and potentially other benefits). * Improvements to diagnostics parsing. Better error handling, and fallback handling. Added ability to parse downstream compilers without a prefix. Added ability to parse Slang with a prefix. * DownstreamDiagnostic::Type -> Severity and related fixes. * Small fixes around moving from DownstreamDiagnostic::Type -> Severity * Fix handling of 'special intrinsic' expansion * Split out the handling of intrinsic expansion into it's own type and files. * Fixes to reading expected output - for SimpleLine test. * Test using += to check #line output. * A test around += and return. * Small comment fixes. Co-authored-by: Tim Foley <tfoleyNV@users.noreply.github.com>
-rw-r--r--build/visual-studio/slang/slang.vcxproj2
-rw-r--r--build/visual-studio/slang/slang.vcxproj.filters6
-rw-r--r--source/slang/core.meta.slang3
-rw-r--r--source/slang/slang-emit-c-like.cpp479
-rw-r--r--source/slang/slang-emit-c-like.h8
-rw-r--r--source/slang/slang-emit-glsl.cpp2
-rw-r--r--source/slang/slang-intrinsic-expand.cpp519
-rw-r--r--source/slang/slang-intrinsic-expand.h34
-rw-r--r--tests/diagnostics/syntax-error-intrinsic.slang.1.expected1
-rw-r--r--tests/diagnostics/syntax-error-intrinsic.slang.2.expected1
-rw-r--r--tests/diagnostics/syntax-error-intrinsic.slang.3.expected1
-rw-r--r--tests/diagnostics/syntax-error-intrinsic.slang.4.expected1
-rw-r--r--tests/diagnostics/syntax-error-intrinsic.slang.expected2
-rw-r--r--tests/diagnostics/syntax-error-op-line-2.slang33
-rw-r--r--tests/diagnostics/syntax-error-op-line-2.slang.expected1
-rw-r--r--tests/diagnostics/syntax-error-op-line.slang22
-rw-r--r--tests/diagnostics/syntax-error-op-line.slang.expected1
-rw-r--r--tools/slang-test/slang-test-main.cpp72
18 files changed, 690 insertions, 498 deletions
diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj
index 642ddab7a..55233f013 100644
--- a/build/visual-studio/slang/slang.vcxproj
+++ b/build/visual-studio/slang/slang.vcxproj
@@ -219,6 +219,7 @@
<ClInclude Include="..\..\..\source\slang\slang-hlsl-intrinsic-set.h" />
<ClInclude Include="..\..\..\source\slang\slang-image-format-defs.h" />
<ClInclude Include="..\..\..\source\slang\slang-include-system.h" />
+ <ClInclude Include="..\..\..\source\slang\slang-intrinsic-expand.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-any-value-marshalling.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-augment-make-existential.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-bind-existentials.h" />
@@ -345,6 +346,7 @@
<ClCompile Include="..\..\..\source\slang\slang-glsl-extension-tracker.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-hlsl-intrinsic-set.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-include-system.cpp" />
+ <ClCompile Include="..\..\..\source\slang\slang-intrinsic-expand.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-any-value-marshalling.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-augment-make-existential.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-bind-existentials.cpp" />
diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters
index 60b3f0d65..a31688e05 100644
--- a/build/visual-studio/slang/slang.vcxproj.filters
+++ b/build/visual-studio/slang/slang.vcxproj.filters
@@ -108,6 +108,9 @@
<ClInclude Include="..\..\..\source\slang\slang-include-system.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang\slang-intrinsic-expand.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang\slang-ir-any-value-marshalling.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -482,6 +485,9 @@
<ClCompile Include="..\..\..\source\slang\slang-include-system.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\slang\slang-intrinsic-expand.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\slang\slang-ir-any-value-marshalling.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index a60da422c..55f6c607b 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -1921,11 +1921,12 @@ int getStringHash(String string);
// Use will produce a syntax error in downstream compiler
// Useful for testing diagnostics around compilation errors of downstream compiler
+// It 'returns' an int so can be used in expressions without the front end complaining.
__target_intrinsic(hlsl, " @ ")
__target_intrinsic(glsl, " @ ")
__target_intrinsic(cuda, " @ ")
__target_intrinsic(cpp, " @ ")
-void __SyntaxError();
+int __SyntaxError();
// Operators to apply to `enum` types
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index d21e16186..d916019c7 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -22,6 +22,8 @@
#include "slang-type-layout.h"
#include "slang-visitor.h"
+#include "slang-intrinsic-expand.h"
+
#include "slang-emit-source-writer.h"
#include "slang-mangled-lexer.h"
@@ -1417,481 +1419,8 @@ void CLikeSourceEmitter::emitIntrinsicCallExprImpl(
}
else
{
- int openParenCount = 0;
-
- const auto returnType = inst->getDataType();
-
- // If it returns void -> then we don't need parenthesis
- if (as<IRVoidType>(returnType) == nullptr)
- {
- m_writer->emit("(");
- openParenCount++;
- }
-
- // General case: we are going to emit some more complex text.
-
- char const* cursor = name.begin();
- char const* end = name.end();
- while(cursor != end)
- {
- char c = *cursor++;
- if( c != '$' )
- {
- // Not an escape sequence
- m_writer->emitRawTextSpan(&c, &c+1);
- continue;
- }
-
- SLANG_RELEASE_ASSERT(cursor != end);
-
- char d = *cursor++;
-
- switch (d)
- {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- {
- // Simple case: emit one of the direct arguments to the call
- Index argIndex = d - '0';
- SLANG_RELEASE_ASSERT((0 <= argIndex) && (argIndex < argCount));
- m_writer->emit("(");
- emitOperand(args[argIndex].get(), getInfo(EmitOp::General));
- m_writer->emit(")");
- }
- break;
-
- case 'T':
- // Get the the 'element' type for the type of the param at the index
- {
- SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9');
- Index argIndex = (*cursor++) - '0';
- SLANG_RELEASE_ASSERT(argCount > argIndex);
-
- IRType* type = args[argIndex].get()->getDataType();
- if (auto baseTextureType = as<IRTextureType>(type))
- {
- type = baseTextureType->getElementType();
- }
- emitType(type);
- }
- break;
-
- case 'S':
- // Get the scalar type of a generic at specified index
- {
- SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9');
- Index argIndex = (*cursor++) - '0';
- SLANG_RELEASE_ASSERT(argCount > argIndex);
-
- IRType* type = args[argIndex].get()->getDataType();
- if (auto baseTextureType = as<IRTextureType>(type))
- {
- type = baseTextureType->getElementType();
- }
-
- IRBasicType* underlyingType = nullptr;
- if (auto basicType = as<IRBasicType>(type))
- {
- underlyingType = basicType;
- }
- else if (auto vectorType = as<IRVectorType>(type))
- {
- underlyingType = as<IRBasicType>(vectorType->getElementType());
- }
- else if (auto matrixType = as<IRMatrixType>(type))
- {
- underlyingType = as<IRBasicType>(matrixType->getElementType());
- }
-
- SLANG_ASSERT(underlyingType);
-
- emitSimpleType(underlyingType);
- }
- break;
- case 'p':
- {
- // If we are calling a D3D texturing operation in the form t.Foo(s, ...),
- // then this form will pair up the t and s arguments as needed for a GLSL
- // texturing operation.
- SLANG_RELEASE_ASSERT(argCount >= 2);
-
- auto textureArg = args[0].get();
- auto samplerArg = args[1].get();
-
- if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType()))
- {
- emitTextureOrTextureSamplerTypeImpl(baseTextureType, "sampler");
-
- if (auto samplerType = as<IRSamplerStateTypeBase>(samplerArg->getDataType()))
- {
- if (as<IRSamplerComparisonStateType>(samplerType))
- {
- m_writer->emit("Shadow");
- }
- }
-
- m_writer->emit("(");
- emitOperand(textureArg, getInfo(EmitOp::General));
- m_writer->emit(",");
- emitOperand(samplerArg, getInfo(EmitOp::General));
- m_writer->emit(")");
- }
- else
- {
- SLANG_UNEXPECTED("bad format in intrinsic definition");
- }
- }
- break;
-
- case 'c':
- {
- // When doing texture access in glsl the result may need to be cast.
- // In particular if the underlying texture is 'half' based, glsl only accesses (read/write)
- // as float. So we need to cast to a half type on output.
- // When storing into a texture it is still the case the value written must be half - but
- // we don't need to do any casting there as half is coerced to float without a problem.
- SLANG_RELEASE_ASSERT(argCount >= 1);
-
- auto textureArg = args[0].get();
- if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType()))
- {
- auto elementType = baseTextureType->getElementType();
- IRBasicType* underlyingType = nullptr;
- if (auto basicType = as<IRBasicType>(elementType))
- {
- underlyingType = basicType;
- }
- else if (auto vectorType = as<IRVectorType>(elementType))
- {
- underlyingType = as<IRBasicType>(vectorType->getElementType());
- }
-
- // We only need to output a cast if the underlying type is half.
- if (underlyingType && underlyingType->op == kIROp_HalfType)
- {
- emitSimpleType(elementType);
- m_writer->emit("(");
- openParenCount++;
- }
- }
- }
- break;
-
- case 'z':
- {
- // If we are calling a D3D texturing operation in the form t.Foo(s, ...),
- // where `t` is a `Texture*<T>`, then this is the step where we try to
- // properly swizzle the output of the equivalent GLSL call into the right
- // shape.
- SLANG_RELEASE_ASSERT(argCount >= 1);
-
- auto textureArg = args[0].get();
- if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType()))
- {
- auto elementType = baseTextureType->getElementType();
- if (auto basicType = as<IRBasicType>(elementType))
- {
- // A scalar result is expected
- m_writer->emit(".x");
- }
- else if (auto vectorType = as<IRVectorType>(elementType))
- {
- // A vector result is expected
- auto elementCount = getIntVal(vectorType->getElementCount());
-
- if (elementCount < 4)
- {
- char const* swiz[] = { "", ".x", ".xy", ".xyz", "" };
- m_writer->emit(swiz[elementCount]);
- }
- }
- else
- {
- // What other cases are possible?
- }
- }
- else
- {
- SLANG_UNEXPECTED("bad format in intrinsic definition");
- }
- }
- break;
-
- case 'N':
- {
- // Extract the element count from a vector argument so that
- // we can use it in the constructed expression.
-
- SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9');
- Index argIndex = (*cursor++) - '0';
- SLANG_RELEASE_ASSERT(argCount > argIndex);
-
- auto vectorArg = args[argIndex].get();
- if (auto vectorType = as<IRVectorType>(vectorArg->getDataType()))
- {
- auto elementCount = getIntVal(vectorType->getElementCount());
- m_writer->emit(elementCount);
- }
- else
- {
- SLANG_UNEXPECTED("bad format in intrinsic definition");
- }
- }
- break;
-
- case 'V':
- {
- // Take an argument of some scalar/vector type and pad
- // it out to a 4-vector with the same element type
- // (this is the inverse of `$z`).
- //
- SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9');
- Index argIndex = (*cursor++) - '0';
- SLANG_RELEASE_ASSERT(argCount > argIndex);
-
- auto arg = args[argIndex].get();
- IRIntegerValue elementCount = 1;
- IRType* elementType = arg->getDataType();
- if (auto vectorType = as<IRVectorType>(elementType))
- {
- elementCount = getIntVal(vectorType->getElementCount());
- elementType = vectorType->getElementType();
- }
-
- if(elementCount == 4)
- {
- // In the simple case, the operand is already a 4-vector,
- // so we can just emit it as-is.
- emitOperand(arg, getInfo(EmitOp::General));
- }
- else
- {
- // Otherwise, we need to construct a 4-vector from the
- // value we have, padding it out with zero elements as
- // needed.
- //
- emitVectorTypeName(elementType, 4);
- m_writer->emit("(");
- emitOperand(arg, getInfo(EmitOp::General));
- for(IRIntegerValue ii = elementCount; ii < 4; ++ii)
- {
- m_writer->emit(", ");
- if(getSourceLanguage() == SourceLanguage::GLSL)
- {
- emitSimpleType(elementType);
- m_writer->emit("(0)");
- }
- else
- {
- m_writer->emit("0");
- }
- }
- m_writer->emit(")");
- }
- }
- break;
-
- case 'a':
- {
- // We have an operation that needs to lower to either
- // `atomic*` or `imageAtomic*` for GLSL, depending on
- // whether its first operand is a subscript into an
- // array. This `$a` is the first `a` in `atomic`,
- // so we will replace it accordingly.
- //
- // TODO: This distinction should be made earlier,
- // with the front-end picking the right overload
- // based on the "address space" of the argument.
-
- Index argIndex = 0;
- SLANG_RELEASE_ASSERT(argCount > argIndex);
-
- auto arg = args[argIndex].get();
- if(arg->op == kIROp_ImageSubscript)
- {
- m_writer->emit("imageA");
- }
- else
- {
- m_writer->emit("a");
- }
- }
- break;
-
- case 'A':
- {
- // We have an operand that represents the destination
- // of an atomic operation in GLSL, and it should
- // be lowered based on whether it is an ordinary l-value,
- // or an image subscript. In the image subscript case
- // this operand will turn into multiple arguments
- // to the `imageAtomic*` function.
- //
-
- Index argIndex = 0;
- SLANG_RELEASE_ASSERT(argCount > argIndex);
-
- auto arg = args[argIndex].get();
- if(arg->op == kIROp_ImageSubscript)
- {
- if(getSourceLanguage() == SourceLanguage::GLSL)
- {
- // TODO: we don't handle the multisample
- // case correctly here, where the last
- // component of the image coordinate needs
- // to be broken out into its own argument.
- //
- m_writer->emit("(");
- emitOperand(arg->getOperand(0), getInfo(EmitOp::General));
- m_writer->emit("), ");
-
- // The coordinate argument will have been computed
- // as a `vector<uint, N>` because that is how the
- // HLSL image subscript operations are defined.
- // In contrast, the GLSL `imageAtomic*` operations
- // expect `vector<int, N>` coordinates, so we
- // will hackily insert the conversion here as
- // part of the intrinsic op.
- //
- auto coords = arg->getOperand(1);
- auto coordsType = coords->getDataType();
-
- auto coordsVecType = as<IRVectorType>(coordsType);
- IRIntegerValue elementCount = 1;
- if(coordsVecType)
- {
- coordsType = coordsVecType->getElementType();
- elementCount = getIntVal(coordsVecType->getElementCount());
- }
-
- SLANG_ASSERT(coordsType->op == kIROp_UIntType);
-
- if (elementCount > 1)
- {
- m_writer->emit("ivec");
- m_writer->emit(elementCount);
- }
- else
- {
- m_writer->emit("int");
- }
-
- m_writer->emit("(");
- emitOperand(arg->getOperand(1), getInfo(EmitOp::General));
- m_writer->emit(")");
- }
- else
- {
- m_writer->emit("(");
- emitOperand(arg, getInfo(EmitOp::General));
- m_writer->emit(")");
- }
- }
- else
- {
- m_writer->emit("(");
- emitOperand(arg, getInfo(EmitOp::General));
- m_writer->emit(")");
- }
- }
- break;
-
- // We will use the `$X` case as a prefix for
- // special logic needed when cross-compiling ray-tracing
- // shaders.
- case 'X':
- {
- SLANG_RELEASE_ASSERT(*cursor);
- switch(*cursor++)
- {
- case 'P':
- {
- // The `$XP` case handles looking up
- // the associated `location` for a variable
- // used as the argument ray payload at a
- // trace call site.
-
- Index argIndex = 0;
- SLANG_RELEASE_ASSERT(argCount > argIndex);
- auto arg = args[argIndex].get();
- auto argLoad = as<IRLoad>(arg);
- SLANG_RELEASE_ASSERT(argLoad);
- auto argVar = argLoad->getOperand(0);
- m_writer->emit(getRayPayloadLocation(argVar));
- }
- break;
-
- case 'C':
- {
- // The `$XC` case handles looking up
- // the associated `location` for a variable
- // used as the argument callable payload at a
- // call site.
-
- Index argIndex = 0;
- SLANG_RELEASE_ASSERT(argCount > argIndex);
- auto arg = args[argIndex].get();
- auto argLoad = as<IRLoad>(arg);
- SLANG_RELEASE_ASSERT(argLoad);
- auto argVar = argLoad->getOperand(0);
- m_writer->emit(getCallablePayloadLocation(argVar));
- }
- break;
-
- default:
- SLANG_RELEASE_ASSERT(false);
- break;
- }
- }
- break;
-
- case 'P':
- // Type-based prefix as used for CUDA and C++ targets
- {
- Index argIndex = 0;
- SLANG_RELEASE_ASSERT(argCount > argIndex);
- auto arg = args[argIndex].get();
- auto argType = arg->getDataType();
-
- const char* str = "";
- switch(argType->op)
- {
- #define CASE(OP, STR) \
- case kIROp_##OP: str = #STR; break
-
- CASE(Int8Type, I8);
- CASE(Int16Type, I16);
- CASE(IntType, I32);
- CASE(Int64Type, I64);
- CASE(UInt8Type, U8);
- CASE(UInt16Type, U16);
- CASE(UIntType, U32);
- CASE(UInt64Type, U64);
- CASE(HalfType, F16);
- CASE(FloatType, F32);
- CASE(DoubleType, F64);
-
- #undef CASE
-
- default:
- SLANG_UNEXPECTED("unexpected type in intrinsic definition");
- break;
- }
- m_writer->emit(str);
- }
- break;
-
- default:
- SLANG_UNEXPECTED("bad format in intrinsic definition");
- break;
- }
- }
-
- // Close any remaining open parens
- for (; openParenCount > 0; --openParenCount)
- {
- m_writer->emit(")");
- }
+ IntrinsicExpandContext context(this);
+ context.emit(inst, args, argCount, name);
}
}
diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h
index b9bddca69..5846d99fd 100644
--- a/source/slang/slang-emit-c-like.h
+++ b/source/slang/slang-emit-c-like.h
@@ -100,6 +100,9 @@ public:
/// Get the source manager
SourceManager* getSourceManager() { return m_compileRequest->getSourceManager(); }
+ /// Get the source writer used
+ SourceWriter* getSourceWriter() const { return m_writer; }
+
/// Get the diagnostic sink
DiagnosticSink* getSink() { return m_compileRequest->getSink();}
LineDirectiveMode getLineDirectiveMode() { return m_compileRequest->getLineDirectiveMode(); }
@@ -299,6 +302,8 @@ public:
void emitVectorTypeName(IRType* elementType, IRIntegerValue elementCount) { emitVectorTypeNameImpl(elementType, elementCount); }
+ void emitTextureOrTextureSamplerType(IRTextureTypeBase* type, char const* baseName) { emitTextureOrTextureSamplerTypeImpl(type, baseName); }
+
virtual RefObject* getExtensionTracker() { return nullptr; }
/// Gets a source language for a target for a target. Returns Unknown if not a known target
@@ -313,6 +318,8 @@ public:
protected:
+
+
virtual bool doesTargetSupportPtrTypes() { return false; }
virtual void emitLayoutSemanticsImpl(IRInst* inst, char const* uniformSemanticSpelling = "register") { SLANG_UNUSED(inst); SLANG_UNUSED(uniformSemanticSpelling); }
virtual void emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type) = 0;
@@ -369,7 +376,6 @@ public:
// Emit the argument list (including paranthesis) in a `CallInst`
void _emitCallArgList(IRCall* call);
-
String _generateUniqueName(const UnownedStringSlice& slice);
// Sort witnessTable entries according to the order defined in the witnessed interface type.
diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp
index 2a28be70a..5fb7bd1d7 100644
--- a/source/slang/slang-emit-glsl.cpp
+++ b/source/slang/slang-emit-glsl.cpp
@@ -848,7 +848,7 @@ void GLSLSourceEmitter::emitEntryPointAttributesImpl(IRFunc* irFunc, IREntryPoin
m_writer->emit(" = ");
m_writer->emit(sizeAlongAxis[ii]);
}
- m_writer->emit(") in;");
+ m_writer->emit(") in;\n");
}
break;
case Stage::Geometry:
diff --git a/source/slang/slang-intrinsic-expand.cpp b/source/slang/slang-intrinsic-expand.cpp
new file mode 100644
index 000000000..ca52dd5f0
--- /dev/null
+++ b/source/slang/slang-intrinsic-expand.cpp
@@ -0,0 +1,519 @@
+// slang-intrinsic-expand.cpp
+#include "slang-intrinsic-expand.h"
+
+namespace Slang {
+
+void IntrinsicExpandContext::emit(IRCall* inst, IRUse* args, Int argCount, const UnownedStringSlice& intrinsicText)
+{
+ m_args = args;
+ m_argCount = argCount;
+ m_text = intrinsicText;
+
+ const auto returnType = inst->getDataType();
+
+ // If it returns void -> then we don't need parenthesis
+ if (as<IRVoidType>(returnType) == nullptr)
+ {
+ m_writer->emit("(");
+ m_openParenCount++;
+ }
+
+ {
+ char const* spanStart = intrinsicText.begin();
+ char const* cursor = spanStart;
+ char const* end = intrinsicText.end();
+
+ while (cursor < end)
+ {
+ // Indicates the start of a 'special' sequence
+ if (*cursor == '$')
+ {
+ // Flush any chars not yet output
+ if (spanStart < cursor)
+ {
+ m_writer->emit(spanStart, cursor);
+ }
+
+ // Requires special processing for output (ie we don't just copy chars verbatim)
+ cursor = _emitSpecial(cursor);
+ // The start is now after 'special' handling
+ spanStart = cursor;
+ }
+ else
+ {
+ cursor++;
+ }
+ }
+
+ // Flush any non escaped
+ if (spanStart < end)
+ {
+ m_writer->emit(spanStart, end);
+ }
+ }
+
+ // Close any remaining open parens
+ for (Index i = 0; i < m_openParenCount; ++i)
+ {
+ m_writer->emit(")");
+ }
+}
+
+const char* IntrinsicExpandContext::_emitSpecial(const char* cursor)
+{
+ const char*const end = m_text.end();
+
+ // Check we are at start of 'special' sequence
+ SLANG_ASSERT(*cursor == '$');
+ cursor++;
+
+ SLANG_RELEASE_ASSERT(cursor < end);
+
+ const char d = *cursor++;
+
+ switch (d)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ // Simple case: emit one of the direct arguments to the call
+ Index argIndex = d - '0';
+ SLANG_RELEASE_ASSERT((0 <= argIndex) && (argIndex < m_argCount));
+ m_writer->emit("(");
+ m_emitter->emitOperand(m_args[argIndex].get(), getInfo(EmitOp::General));
+ m_writer->emit(")");
+ }
+ break;
+
+ case 'T':
+ // Get the the 'element' type for the type of the param at the index
+ {
+ SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9');
+ Index argIndex = (*cursor++) - '0';
+ SLANG_RELEASE_ASSERT(m_argCount > argIndex);
+
+ IRType* type = m_args[argIndex].get()->getDataType();
+ if (auto baseTextureType = as<IRTextureType>(type))
+ {
+ type = baseTextureType->getElementType();
+ }
+ m_emitter->emitType(type);
+ }
+ break;
+
+ case 'S':
+ // Get the scalar type of a generic at specified index
+ {
+ SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9');
+ Index argIndex = (*cursor++) - '0';
+ SLANG_RELEASE_ASSERT(m_argCount > argIndex);
+
+ IRType* type = m_args[argIndex].get()->getDataType();
+ if (auto baseTextureType = as<IRTextureType>(type))
+ {
+ type = baseTextureType->getElementType();
+ }
+
+ IRBasicType* underlyingType = nullptr;
+ if (auto basicType = as<IRBasicType>(type))
+ {
+ underlyingType = basicType;
+ }
+ else if (auto vectorType = as<IRVectorType>(type))
+ {
+ underlyingType = as<IRBasicType>(vectorType->getElementType());
+ }
+ else if (auto matrixType = as<IRMatrixType>(type))
+ {
+ underlyingType = as<IRBasicType>(matrixType->getElementType());
+ }
+
+ SLANG_ASSERT(underlyingType);
+
+ m_emitter->emitSimpleType(underlyingType);
+ }
+ break;
+ case 'p':
+ {
+ // If we are calling a D3D texturing operation in the form t.Foo(s, ...),
+ // then this form will pair up the t and s arguments as needed for a GLSL
+ // texturing operation.
+ SLANG_RELEASE_ASSERT(m_argCount >= 2);
+
+ auto textureArg = m_args[0].get();
+ auto samplerArg = m_args[1].get();
+
+ if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType()))
+ {
+ m_emitter->emitTextureOrTextureSamplerType(baseTextureType, "sampler");
+
+ if (auto samplerType = as<IRSamplerStateTypeBase>(samplerArg->getDataType()))
+ {
+ if (as<IRSamplerComparisonStateType>(samplerType))
+ {
+ m_writer->emit("Shadow");
+ }
+ }
+
+ m_writer->emit("(");
+ m_emitter->emitOperand(textureArg, getInfo(EmitOp::General));
+ m_writer->emit(",");
+ m_emitter->emitOperand(samplerArg, getInfo(EmitOp::General));
+ m_writer->emit(")");
+ }
+ else
+ {
+ SLANG_UNEXPECTED("bad format in intrinsic definition");
+ }
+ }
+ break;
+
+ case 'c':
+ {
+ // When doing texture access in glsl the result may need to be cast.
+ // In particular if the underlying texture is 'half' based, glsl only accesses (read/write)
+ // as float. So we need to cast to a half type on output.
+ // When storing into a texture it is still the case the value written must be half - but
+ // we don't need to do any casting there as half is coerced to float without a problem.
+ SLANG_RELEASE_ASSERT(m_argCount >= 1);
+
+ auto textureArg = m_args[0].get();
+ if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType()))
+ {
+ auto elementType = baseTextureType->getElementType();
+ IRBasicType* underlyingType = nullptr;
+ if (auto basicType = as<IRBasicType>(elementType))
+ {
+ underlyingType = basicType;
+ }
+ else if (auto vectorType = as<IRVectorType>(elementType))
+ {
+ underlyingType = as<IRBasicType>(vectorType->getElementType());
+ }
+
+ // We only need to output a cast if the underlying type is half.
+ if (underlyingType && underlyingType->op == kIROp_HalfType)
+ {
+ m_emitter->emitSimpleType(elementType);
+ m_writer->emit("(");
+ m_openParenCount++;
+ }
+ }
+ }
+ break;
+
+ case 'z':
+ {
+ // If we are calling a D3D texturing operation in the form t.Foo(s, ...),
+ // where `t` is a `Texture*<T>`, then this is the step where we try to
+ // properly swizzle the output of the equivalent GLSL call into the right
+ // shape.
+ SLANG_RELEASE_ASSERT(m_argCount >= 1);
+
+ auto textureArg = m_args[0].get();
+ if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType()))
+ {
+ auto elementType = baseTextureType->getElementType();
+ if (auto basicType = as<IRBasicType>(elementType))
+ {
+ // A scalar result is expected
+ m_writer->emit(".x");
+ }
+ else if (auto vectorType = as<IRVectorType>(elementType))
+ {
+ // A vector result is expected
+ auto elementCount = getIntVal(vectorType->getElementCount());
+
+ if (elementCount < 4)
+ {
+ char const* swiz[] = { "", ".x", ".xy", ".xyz", "" };
+ m_writer->emit(swiz[elementCount]);
+ }
+ }
+ else
+ {
+ // What other cases are possible?
+ }
+ }
+ else
+ {
+ SLANG_UNEXPECTED("bad format in intrinsic definition");
+ }
+ }
+ break;
+
+ case 'N':
+ {
+ // Extract the element count from a vector argument so that
+ // we can use it in the constructed expression.
+
+ SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9');
+ Index argIndex = (*cursor++) - '0';
+ SLANG_RELEASE_ASSERT(m_argCount > argIndex);
+
+ auto vectorArg = m_args[argIndex].get();
+ if (auto vectorType = as<IRVectorType>(vectorArg->getDataType()))
+ {
+ auto elementCount = getIntVal(vectorType->getElementCount());
+ m_writer->emit(elementCount);
+ }
+ else
+ {
+ SLANG_UNEXPECTED("bad format in intrinsic definition");
+ }
+ }
+ break;
+
+ case 'V':
+ {
+ // Take an argument of some scalar/vector type and pad
+ // it out to a 4-vector with the same element type
+ // (this is the inverse of `$z`).
+ //
+ SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9');
+ Index argIndex = (*cursor++) - '0';
+ SLANG_RELEASE_ASSERT(m_argCount > argIndex);
+
+ auto arg = m_args[argIndex].get();
+ IRIntegerValue elementCount = 1;
+ IRType* elementType = arg->getDataType();
+ if (auto vectorType = as<IRVectorType>(elementType))
+ {
+ elementCount = getIntVal(vectorType->getElementCount());
+ elementType = vectorType->getElementType();
+ }
+
+ if (elementCount == 4)
+ {
+ // In the simple case, the operand is already a 4-vector,
+ // so we can just emit it as-is.
+ m_emitter->emitOperand(arg, getInfo(EmitOp::General));
+ }
+ else
+ {
+ // Otherwise, we need to construct a 4-vector from the
+ // value we have, padding it out with zero elements as
+ // needed.
+ //
+ m_emitter->emitVectorTypeName(elementType, 4);
+ m_writer->emit("(");
+ m_emitter->emitOperand(arg, getInfo(EmitOp::General));
+ for (IRIntegerValue ii = elementCount; ii < 4; ++ii)
+ {
+ m_writer->emit(", ");
+ if (m_emitter->getSourceLanguage() == SourceLanguage::GLSL)
+ {
+ m_emitter->emitSimpleType(elementType);
+ m_writer->emit("(0)");
+ }
+ else
+ {
+ m_writer->emit("0");
+ }
+ }
+ m_writer->emit(")");
+ }
+ }
+ break;
+
+ case 'a':
+ {
+ // We have an operation that needs to lower to either
+ // `atomic*` or `imageAtomic*` for GLSL, depending on
+ // whether its first operand is a subscript into an
+ // array. This `$a` is the first `a` in `atomic`,
+ // so we will replace it accordingly.
+ //
+ // TODO: This distinction should be made earlier,
+ // with the front-end picking the right overload
+ // based on the "address space" of the argument.
+
+ Index argIndex = 0;
+ SLANG_RELEASE_ASSERT(m_argCount > argIndex);
+
+ auto arg = m_args[argIndex].get();
+ if (arg->op == kIROp_ImageSubscript)
+ {
+ m_writer->emit("imageA");
+ }
+ else
+ {
+ m_writer->emit("a");
+ }
+ }
+ break;
+
+ case 'A':
+ {
+ // We have an operand that represents the destination
+ // of an atomic operation in GLSL, and it should
+ // be lowered based on whether it is an ordinary l-value,
+ // or an image subscript. In the image subscript case
+ // this operand will turn into multiple arguments
+ // to the `imageAtomic*` function.
+ //
+
+ Index argIndex = 0;
+ SLANG_RELEASE_ASSERT(m_argCount > argIndex);
+
+ auto arg = m_args[argIndex].get();
+ if (arg->op == kIROp_ImageSubscript)
+ {
+ if (m_emitter->getSourceLanguage() == SourceLanguage::GLSL)
+ {
+ // TODO: we don't handle the multisample
+ // case correctly here, where the last
+ // component of the image coordinate needs
+ // to be broken out into its own argument.
+ //
+ m_writer->emit("(");
+ m_emitter->emitOperand(arg->getOperand(0), getInfo(EmitOp::General));
+ m_writer->emit("), ");
+
+ // The coordinate argument will have been computed
+ // as a `vector<uint, N>` because that is how the
+ // HLSL image subscript operations are defined.
+ // In contrast, the GLSL `imageAtomic*` operations
+ // expect `vector<int, N>` coordinates, so we
+ // will hackily insert the conversion here as
+ // part of the intrinsic op.
+ //
+ auto coords = arg->getOperand(1);
+ auto coordsType = coords->getDataType();
+
+ auto coordsVecType = as<IRVectorType>(coordsType);
+ IRIntegerValue elementCount = 1;
+ if (coordsVecType)
+ {
+ coordsType = coordsVecType->getElementType();
+ elementCount = getIntVal(coordsVecType->getElementCount());
+ }
+
+ SLANG_ASSERT(coordsType->op == kIROp_UIntType);
+
+ if (elementCount > 1)
+ {
+ m_writer->emit("ivec");
+ m_writer->emit(elementCount);
+ }
+ else
+ {
+ m_writer->emit("int");
+ }
+
+ m_writer->emit("(");
+ m_emitter->emitOperand(arg->getOperand(1), getInfo(EmitOp::General));
+ m_writer->emit(")");
+ }
+ else
+ {
+ m_writer->emit("(");
+ m_emitter->emitOperand(arg, getInfo(EmitOp::General));
+ m_writer->emit(")");
+ }
+ }
+ else
+ {
+ m_writer->emit("(");
+ m_emitter->emitOperand(arg, getInfo(EmitOp::General));
+ m_writer->emit(")");
+ }
+ }
+ break;
+
+ // We will use the `$X` case as a prefix for
+ // special logic needed when cross-compiling ray-tracing
+ // shaders.
+ case 'X':
+ {
+ SLANG_RELEASE_ASSERT(*cursor);
+ switch (*cursor++)
+ {
+ case 'P':
+ {
+ // The `$XP` case handles looking up
+ // the associated `location` for a variable
+ // used as the argument ray payload at a
+ // trace call site.
+
+ Index argIndex = 0;
+ SLANG_RELEASE_ASSERT(m_argCount > argIndex);
+ auto arg = m_args[argIndex].get();
+ auto argLoad = as<IRLoad>(arg);
+ SLANG_RELEASE_ASSERT(argLoad);
+ auto argVar = argLoad->getOperand(0);
+ m_writer->emit(m_emitter->getRayPayloadLocation(argVar));
+ }
+ break;
+
+ case 'C':
+ {
+ // The `$XC` case handles looking up
+ // the associated `location` for a variable
+ // used as the argument callable payload at a
+ // call site.
+
+ Index argIndex = 0;
+ SLANG_RELEASE_ASSERT(m_argCount > argIndex);
+ auto arg = m_args[argIndex].get();
+ auto argLoad = as<IRLoad>(arg);
+ SLANG_RELEASE_ASSERT(argLoad);
+ auto argVar = argLoad->getOperand(0);
+ m_writer->emit(m_emitter->getCallablePayloadLocation(argVar));
+ }
+ break;
+
+ default:
+ SLANG_RELEASE_ASSERT(false);
+ break;
+ }
+ }
+ break;
+
+ case 'P':
+ // Type-based prefix as used for CUDA and C++ targets
+ {
+ Index argIndex = 0;
+ SLANG_RELEASE_ASSERT(m_argCount > argIndex);
+ auto arg = m_args[argIndex].get();
+ auto argType = arg->getDataType();
+
+ const char* str = "";
+ switch (argType->op)
+ {
+#define CASE(OP, STR) \
+ case kIROp_##OP: str = #STR; break
+
+ CASE(Int8Type, I8);
+ CASE(Int16Type, I16);
+ CASE(IntType, I32);
+ CASE(Int64Type, I64);
+ CASE(UInt8Type, U8);
+ CASE(UInt16Type, U16);
+ CASE(UIntType, U32);
+ CASE(UInt64Type, U64);
+ CASE(HalfType, F16);
+ CASE(FloatType, F32);
+ CASE(DoubleType, F64);
+
+#undef CASE
+
+ default:
+ SLANG_UNEXPECTED("unexpected type in intrinsic definition");
+ break;
+ }
+ m_writer->emit(str);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("bad format in intrinsic definition");
+ break;
+ }
+
+ // Return the cursor position. Will be next character after characters processed
+ // for the 'special'
+ return cursor;
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-intrinsic-expand.h b/source/slang/slang-intrinsic-expand.h
new file mode 100644
index 000000000..468e1f80a
--- /dev/null
+++ b/source/slang/slang-intrinsic-expand.h
@@ -0,0 +1,34 @@
+// slang-intrinsic-expand.h
+#ifndef SLANG_INTRINSIC_EXPAND_H
+#define SLANG_INTRINSIC_EXPAND_H
+
+#include "slang-emit-c-like.h"
+
+namespace Slang
+{
+
+/* Handles all the special case handling of expansions of intrinsics. In particular handles the expansion
+of the 'special cases' prefixed with '$' */
+struct IntrinsicExpandContext
+{
+ IntrinsicExpandContext(CLikeSourceEmitter* emitter) :
+ m_emitter(emitter),
+ m_writer(emitter->getSourceWriter())
+ {
+ }
+
+ void emit(IRCall* inst, IRUse* args, Int argCount, const UnownedStringSlice& intrinsicText);
+
+protected:
+ const char* _emitSpecial(const char* cursor);
+
+ SourceWriter* m_writer;
+ UnownedStringSlice m_text;
+ IRUse* m_args = nullptr;
+ Int m_argCount = 0;
+ Index m_openParenCount = 0;
+ CLikeSourceEmitter* m_emitter;
+};
+
+}
+#endif
diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.1.expected b/tests/diagnostics/syntax-error-intrinsic.slang.1.expected
deleted file mode 100644
index 3c032078a..000000000
--- a/tests/diagnostics/syntax-error-intrinsic.slang.1.expected
+++ /dev/null
@@ -1 +0,0 @@
-18
diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.2.expected b/tests/diagnostics/syntax-error-intrinsic.slang.2.expected
deleted file mode 100644
index 3c032078a..000000000
--- a/tests/diagnostics/syntax-error-intrinsic.slang.2.expected
+++ /dev/null
@@ -1 +0,0 @@
-18
diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.3.expected b/tests/diagnostics/syntax-error-intrinsic.slang.3.expected
deleted file mode 100644
index 98d9bcb75..000000000
--- a/tests/diagnostics/syntax-error-intrinsic.slang.3.expected
+++ /dev/null
@@ -1 +0,0 @@
-17
diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.4.expected b/tests/diagnostics/syntax-error-intrinsic.slang.4.expected
deleted file mode 100644
index 98d9bcb75..000000000
--- a/tests/diagnostics/syntax-error-intrinsic.slang.4.expected
+++ /dev/null
@@ -1 +0,0 @@
-17
diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.expected b/tests/diagnostics/syntax-error-intrinsic.slang.expected
index 98d9bcb75..d6b24041c 100644
--- a/tests/diagnostics/syntax-error-intrinsic.slang.expected
+++ b/tests/diagnostics/syntax-error-intrinsic.slang.expected
@@ -1 +1 @@
-17
+19
diff --git a/tests/diagnostics/syntax-error-op-line-2.slang b/tests/diagnostics/syntax-error-op-line-2.slang
new file mode 100644
index 000000000..994e875a1
--- /dev/null
+++ b/tests/diagnostics/syntax-error-op-line-2.slang
@@ -0,0 +1,33 @@
+// syntax-error-op-line-2.slang
+
+// NOTE! That although this is a 'diagnostic' like test, it tests using downstream compiler
+// the downstream compiler being present is a requirement, so we mark as a 'TEST' so that
+// those tests are made.
+
+//TEST:SIMPLE_LINE:-entry computeMain -target spirv
+//TEST:SIMPLE_LINE:-entry computeMain -target dxil -profile cs_6_0
+//TEST:SIMPLE_LINE:-entry computeMain -target dxbc
+//TEST:SIMPLE_LINE:-entry computeMain -target dll
+//TEST:SIMPLE_LINE:-entry computeMain -target ptx
+
+// Here the thing being checked is error reporting around return, and +=
+
+int doSomething(int a)
+{
+ a += a;
+
+ return a
+ +=
+ __SyntaxError();
+}
+
+[shader("compute")]
+[numthreads(4, 1, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ int id = int(dispatchThreadID.x);
+
+ int v = int(dispatchThreadID.y);
+
+ v += doSomething(id);
+} \ No newline at end of file
diff --git a/tests/diagnostics/syntax-error-op-line-2.slang.expected b/tests/diagnostics/syntax-error-op-line-2.slang.expected
new file mode 100644
index 000000000..aabe6ec39
--- /dev/null
+++ b/tests/diagnostics/syntax-error-op-line-2.slang.expected
@@ -0,0 +1 @@
+21
diff --git a/tests/diagnostics/syntax-error-op-line.slang b/tests/diagnostics/syntax-error-op-line.slang
new file mode 100644
index 000000000..eab15e4a4
--- /dev/null
+++ b/tests/diagnostics/syntax-error-op-line.slang
@@ -0,0 +1,22 @@
+// syntax-error-op-line.slang
+
+// NOTE! That although this is a 'diagnostic' like test, it tests using downstream compiler
+// the downstream compiler being present is a requirement, so we mark as a 'TEST' so that
+// those tests are made.
+
+//TEST:SIMPLE_LINE:-entry computeMain -target spirv
+//TEST:SIMPLE_LINE:-entry computeMain -target dxil -profile cs_6_0
+//TEST:SIMPLE_LINE:-entry computeMain -target dxbc
+//TEST:SIMPLE_LINE:-entry computeMain -target dll
+//TEST:SIMPLE_LINE:-entry computeMain -target ptx
+
+[shader("compute")]
+[numthreads(4, 1, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ int id = int(dispatchThreadID.x);
+
+ int v = int(dispatchThreadID.y);
+
+ v += id + __SyntaxError();
+} \ No newline at end of file
diff --git a/tests/diagnostics/syntax-error-op-line.slang.expected b/tests/diagnostics/syntax-error-op-line.slang.expected
new file mode 100644
index 000000000..aabe6ec39
--- /dev/null
+++ b/tests/diagnostics/syntax-error-op-line.slang.expected
@@ -0,0 +1 @@
+21
diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp
index 861e42971..d57ea50e4 100644
--- a/tools/slang-test/slang-test-main.cpp
+++ b/tools/slang-test/slang-test-main.cpp
@@ -1155,6 +1155,47 @@ TestResult runSimpleTest(TestContext* context, TestInput& input)
return result;
}
+SlangResult _readText(const UnownedStringSlice& path, String& out)
+{
+ try
+ {
+ out = Slang::File::readAllText(path);
+ }
+ catch (const Slang::IOException&)
+ {
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+}
+
+static SlangResult _readExpected(const UnownedStringSlice& stem, String& out)
+{
+ StringBuilder buf;
+
+ // See if we have a trailing . index, and try *without* that first
+ const Index dotIndex = stem.lastIndexOf('.');
+ if (dotIndex >= 0)
+ {
+ const UnownedStringSlice postfix = stem.tail(dotIndex + 1);
+
+ Int value;
+ if (SLANG_SUCCEEDED(StringUtil::parseInt(postfix, value)))
+ {
+ UnownedStringSlice head = stem.head(dotIndex);
+
+ buf << head << ".expected";
+
+ if (SLANG_SUCCEEDED(_readText(buf.getUnownedSlice(), out)))
+ {
+ return SLANG_OK;
+ }
+ }
+ }
+
+ buf << stem << ".expected";
+ return _readText(buf.getUnownedSlice(), out);
+}
+
TestResult runSimpleLineTest(TestContext* context, TestInput& input)
{
// need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect
@@ -1201,24 +1242,26 @@ TestResult runSimpleLineTest(TestContext* context, TestInput& input)
actualOutput << "No output diagnostics\n";
}
+ TestResult result = TestResult::Fail;
- String expectedOutputPath = outputStem + ".expected";
String expectedOutput;
- try
- {
- expectedOutput = Slang::File::readAllText(expectedOutputPath);
- }
- catch (const Slang::IOException&)
+
+ if (SLANG_SUCCEEDED(_readExpected(outputStem.getUnownedSlice(), expectedOutput)))
{
+ if (StringUtil::areLinesEqual(expectedOutput.getUnownedSlice(), actualOutput.getUnownedSlice()))
+ {
+ result = TestResult::Pass;
+ }
+ else
+ {
+ context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
+ }
}
-
- TestResult result = TestResult::Pass;
-
- // Otherwise we compare to the expected output
- if (!_areResultsEqual(input.testOptions->type, expectedOutput, actualOutput))
+ else
{
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
- result = TestResult::Fail;
+ StringBuilder buf;
+ buf << "Unable to find expected output for '" << outputStem << "'";
+ context->reporter->message(TestMessageType::TestFailure, buf);
}
// If the test failed, then we write the actual output to a file
@@ -1228,8 +1271,6 @@ TestResult runSimpleLineTest(TestContext* context, TestInput& input)
{
String actualOutputPath = outputStem + ".actual";
Slang::File::writeAllText(actualOutputPath, actualOutput);
-
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
}
return result;
@@ -3397,3 +3438,4 @@ int main(int argc, char** argv)
#endif
return SLANG_SUCCEEDED(res) ? 0 : 1;
}
+