From 360d4f7a17a066cc878cdb2c558464bfdeaa3418 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Wed, 17 Feb 2021 19:04:48 -0500 Subject: More #line improvements (#1713) * #include an absolute path didn't work - because paths were taken to always be relative. * WIP: First pass in supporting output of line error information. * Add support for lexing to better be able to indicate SourceLocation information. * Fix lexer usage in DiagnosticSink in C++ extractor. * Update diagnostics tests to have line location info. * Fixed test expected output that now have source location information in them. * Better handling of tab. * Fix test expected results for tabbing change. * DiagnosticLexer -> DiagnosticSink::SourceLocationLexer Added line continuation tests. * Fix typo. * Added String::appendRepeatedChar * Change to rerun tests. * Added source locations to IR dumping. * Output column for IR dump source loc. * Add support for closing brace location to AST. Use closing brace location in lowering when adding return void. * Set the source location through SourceLoc - simplifies identifying if current loc is valid. * Copy terminator sloc. * Test for improved #line handling. * Made writer the last parameter for dumpIR. Small improvements to comments. * Disable sloc output on dump IR by default. * Fix issue with #line and inlining. * Fix for output with improved #line output. * Small comment change - mainly to kick off TC build. Co-authored-by: Tim Foley --- source/slang/slang-ast-stmt.h | 3 ++ source/slang/slang-emit-c-like.cpp | 18 +++++-- source/slang/slang-emit-source-writer.cpp | 61 ++++++++++++++++++----- source/slang/slang-emit-source-writer.h | 10 ++-- source/slang/slang-emit.cpp | 6 ++- source/slang/slang-ir-ssa.cpp | 5 +- source/slang/slang-ir.cpp | 75 +++++++++++++++++++++++------ source/slang/slang-ir.h | 51 ++++++++++++++------ source/slang/slang-lower-to-ir.cpp | 17 ++++++- source/slang/slang-parser.cpp | 35 +++++++++++++- source/slang/slang-source-loc.cpp | 55 +++++++++++++++++++++ source/slang/slang-source-loc.h | 8 +++ tests/compute/half-texture.slang.1.expected | 2 - tests/diagnostics/local-line.slang | 45 +++++++++++++++++ tests/diagnostics/local-line.slang.expected | 1 + 15 files changed, 337 insertions(+), 55 deletions(-) create mode 100644 tests/diagnostics/local-line.slang create mode 100644 tests/diagnostics/local-line.slang.expected diff --git a/source/slang/slang-ast-stmt.h b/source/slang/slang-ast-stmt.h index 07fc9eeb5..20f9e270e 100644 --- a/source/slang/slang-ast-stmt.h +++ b/source/slang/slang-ast-stmt.h @@ -28,6 +28,9 @@ class BlockStmt : public ScopeStmt { SLANG_AST_CLASS(BlockStmt) + /// TODO(JS): Having ranges of sourcelocs might be a good addition to AST nodes in general. + SourceLoc closingSourceLoc; ///< The source location of the closing brace + Stmt* body = nullptr; }; diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 570d48156..0bd6efad3 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -323,9 +323,9 @@ void CLikeSourceEmitter::emitTypeImpl(IRType* type, const StringSliceLoc* nameAn { if (nameAndLoc) { - // TODO(JS): No call to the previous version of this method was passing in a typeLoc, so disabled for - // now for simplicity - //m_writer->advanceToSourceLocation(typeLoc); + // We advance here, such that if there is a #line directive to output it will + // be done so before the type name appears. + m_writer->advanceToSourceLocationIfValid(nameAndLoc->loc); EDeclarator nameDeclarator; nameDeclarator.flavor = EDeclarator::Flavor::Name; @@ -1986,7 +1986,16 @@ void CLikeSourceEmitter::_emitInst(IRInst* inst) return; } - m_writer->advanceToSourceLocation(inst->sourceLoc); + // Specially handle params. The issue here is around PHI nodes, and that they do not + // have source loc information, by default, but we don't want to force outputting a #line. + if (inst->getOp() == kIROp_Param) + { + m_writer->advanceToSourceLocationIfValid(inst->sourceLoc); + } + else + { + m_writer->advanceToSourceLocation(inst->sourceLoc); + } switch(inst->getOp()) { @@ -2694,6 +2703,7 @@ void CLikeSourceEmitter::emitSimpleFuncImpl(IRFunc* func) auto name = getName(func); emitFuncDecorations(func); + emitType(resultType, name); emitSimpleFuncParamsImpl(func); emitSemantics(func); diff --git a/source/slang/slang-emit-source-writer.cpp b/source/slang/slang-emit-source-writer.cpp index 8deecc846..29a83a1fc 100644 --- a/source/slang/slang-emit-source-writer.cpp +++ b/source/slang/slang-emit-source-writer.cpp @@ -152,19 +152,19 @@ void SourceWriter::emit(Name* name) void SourceWriter::emit(const NameLoc& nameAndLoc) { - advanceToSourceLocation(nameAndLoc.loc); + advanceToSourceLocationIfValid(nameAndLoc.loc); emit(getText(nameAndLoc.name)); } void SourceWriter::emit(const StringSliceLoc& nameAndLoc) { - advanceToSourceLocation(nameAndLoc.loc); + advanceToSourceLocationIfValid(nameAndLoc.loc); emit(nameAndLoc.name); } void SourceWriter::emitName(Name* name, const SourceLoc& locIn) { - advanceToSourceLocation(locIn); + advanceToSourceLocationIfValid(locIn); emit(name); } @@ -251,19 +251,58 @@ void SourceWriter::emit(double value) emit(stream.str().c_str()); } -void SourceWriter::advanceToSourceLocation(const SourceLoc& sourceLocation) +void SourceWriter::advanceToSourceLocationIfValid(const SourceLoc& sourceLocation) { - advanceToSourceLocation(getSourceManager()->getHumaneLoc(sourceLocation)); + if (sourceLocation.isValid()) + { + advanceToSourceLocation(sourceLocation); + } } -void SourceWriter::advanceToSourceLocation(const HumaneSourceLoc& sourceLocation) +void SourceWriter::advanceToSourceLocation(const SourceLoc& sourceLocation) { - // Skip invalid locations - if (sourceLocation.line <= 0) + if (getLineDirectiveMode() == LineDirectiveMode::None) + { + // Ignore if we aren't outputting directives + return; + } + + if (!sourceLocation.isValid()) + { + // If it's not valid, we will just keep the current location. + + // The question now is if we want to trigger outputting the source location again. We do so if + // * The nextSourceLoc is valid + // * The line number on the output is different from that location + m_needToUpdateSourceLocation = m_needToUpdateSourceLocation || (m_nextSourceLoc.isValid() && m_nextHumaneSourceLocation.line != m_loc.line); return; + } + + // Even if the source location is the same, we still want to trigger output, as long + // as we have a valid line location. + if (sourceLocation == m_nextSourceLoc) + { + // This is important because we can end up with many instructions with the same source location - for example + // when we have [__unsafeForceInlineEarly] all the inlined instructions get the same location. + // When we output lines of source, the target sources line numbers change, therefore we need to + // output the same #line directive multiple times. + + m_needToUpdateSourceLocation = m_needToUpdateSourceLocation || (m_nextHumaneSourceLocation.line > 0); + return; + } + + // Workout the humane source location. + const HumaneSourceLoc humaneSourceLoc = getSourceManager()->getHumaneLoc(sourceLocation); + + // If the location is valid, mark need to update, and the new location + if (humaneSourceLoc.line > 0) + { + m_needToUpdateSourceLocation = true; + m_nextHumaneSourceLocation = humaneSourceLoc; + } - m_needToUpdateSourceLocation = true; - m_nextSourceLocation = sourceLocation; + // Either way set this as the current source location. + m_nextSourceLoc = sourceLocation; } void SourceWriter::_flushSourceLocationChange() @@ -276,7 +315,7 @@ void SourceWriter::_flushSourceLocationChange() // advances the location, and outputting text is what // triggers this flush operation. m_needToUpdateSourceLocation = false; - _emitLineDirectiveIfNeeded(m_nextSourceLocation); + _emitLineDirectiveIfNeeded(m_nextHumaneSourceLocation); } void SourceWriter::_emitLineDirectiveAndUpdateSourceLocation(const HumaneSourceLoc& sourceLocation) diff --git a/source/slang/slang-emit-source-writer.h b/source/slang/slang-emit-source-writer.h index dfaf40b31..64dc59801 100644 --- a/source/slang/slang-emit-source-writer.h +++ b/source/slang/slang-emit-source-writer.h @@ -55,7 +55,8 @@ public: /// Move the current source location to that specified void advanceToSourceLocation(const SourceLoc& sourceLocation); - void advanceToSourceLocation(const HumaneSourceLoc& sourceLocation); + /// Only advances if the sourceLocation is valid + void advanceToSourceLocationIfValid(const SourceLoc& sourceLocation); /// Get the content as a string String getContent() { return m_builder.ProduceString(); } @@ -96,9 +97,12 @@ protected: // Current source position for tracking purposes... HumaneSourceLoc m_loc; - HumaneSourceLoc m_nextSourceLocation; - bool m_needToUpdateSourceLocation = false; + SourceLoc m_nextSourceLoc; + HumaneSourceLoc m_nextHumaneSourceLocation; + + bool m_needToUpdateSourceLocation = false; + // Are we at the start of a line, so that we should indent // before writing any other text? bool m_isAtStartOfLine = true; diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index c9de26217..dc4709d11 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -136,7 +136,11 @@ static void dumpIRIfEnabled( if(compileRequest->shouldDumpIR) { DiagnosticSinkWriter writer(compileRequest->getSink()); - dumpIR(irModule, &writer, label); + + IRDumpOptions options; + options.sourceManager = compileRequest->getSourceManager(); + + dumpIR(irModule, options, label, &writer); } } diff --git a/source/slang/slang-ir-ssa.cpp b/source/slang/slang-ir-ssa.cpp index 8d561fa05..14198aa0a 100644 --- a/source/slang/slang-ir-ssa.cpp +++ b/source/slang/slang-ir-ssa.cpp @@ -1109,7 +1109,7 @@ void constructSSA(ConstructSSAContext* context) } } - // Some blocks may now need to pass along arguments to their sucessor, + // Some blocks may now need to pass along arguments to their successor, // which have been stored into the `SSABlockInfo::successorArgs` field. for(auto bb : globalVal->getBlocks()) { @@ -1156,6 +1156,9 @@ void constructSSA(ConstructSSAContext* context) // oldTerminator->transferDecorationsTo(newTerminator); + // Copy the source location of the old terminator to the new terminator + newTerminator->sourceLoc = oldTerminator->sourceLoc; + // A terminator better not have uses, so we shouldn't have // to replace them. SLANG_ASSERT(!oldTerminator->firstUse); diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 5a01dd90a..945cf488f 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -4307,8 +4307,10 @@ namespace Slang { StringBuilder* builder = nullptr; int indent = 0; - IRDumpMode mode = IRDumpMode::Simplified; + IRDumpOptions options; + PathInfo lastPathInfo = PathInfo::makeUnknown(); + Dictionary mapValueToName; Dictionary uniqueNameCounters; UInt uniqueIDCounter = 1; @@ -4393,7 +4395,7 @@ namespace Slang // a nice-to-have rather than a maintenance problem // waiting to happen. - // Allow an empty nam + // Allow an empty name // Special case a name that is the empty string, just in case. if(name.getLength() == 0) { @@ -4636,7 +4638,7 @@ namespace Slang // in the "detailed" mode, so that we always // accurately reflect the structure of the IR. // - if(context->mode == IRDumpMode::Detailed) + if(context->options.mode == IRDumpOptions::Mode::Detailed) return false; if(as(inst)) @@ -5044,6 +5046,49 @@ namespace Slang dumpIndent(context); dumpInstBody(context, inst); + + // Output the originating source location + { + SourceManager* sourceManager = context->options.sourceManager; + if (sourceManager && context->options.flags & IRDumpOptions::Flag::SourceLocations) + { + StringBuilder buf; + buf << " loc: "; + + // Output the line number information + if (inst->sourceLoc.isValid()) + { + // Might want to output actual, but nominal is okay for default + const SourceLocType sourceLocType = SourceLocType::Nominal; + + // Get the source location + const HumaneSourceLoc humaneLoc = sourceManager->getHumaneLoc(inst->sourceLoc, sourceLocType); + if (humaneLoc.line >= 0) + { + buf << humaneLoc.line << "," << humaneLoc.column; + + if (humaneLoc.pathInfo != context->lastPathInfo) + { + buf << " "; + // Output the the location + humaneLoc.pathInfo.appendDisplayName(buf); + context->lastPathInfo = humaneLoc.pathInfo; + } + } + else + { + buf << "not found"; + } + } + else + { + buf << "na"; + } + + dump(context, buf.getUnownedSlice()); + } + } + dump(context, "\n"); } @@ -5057,24 +5102,24 @@ namespace Slang } } - void printSlangIRAssembly(StringBuilder& builder, IRModule* module, IRDumpMode mode) + void printSlangIRAssembly(StringBuilder& builder, IRModule* module, const IRDumpOptions& options) { IRDumpContext context; context.builder = &builder; context.indent = 0; - context.mode = mode; - + context.options = options; + dumpIRModule(&context, module); } - void dumpIR(IRInst* globalVal, ISlangWriter* writer, IRDumpMode mode) + void dumpIR(IRInst* globalVal, const IRDumpOptions& options, ISlangWriter* writer) { StringBuilder sb; IRDumpContext context; context.builder = &sb; context.indent = 0; - context.mode = mode; + context.options = options; dumpInst(&context, globalVal); @@ -5082,9 +5127,9 @@ namespace Slang writer->flush(); } - void dumpIR(IRModule* module, ISlangWriter* slangWriter, char const* label) + void dumpIR(IRModule* module, const IRDumpOptions& options, char const* label, ISlangWriter* inWriter) { - WriterHelper writer(slangWriter); + WriterHelper writer(inWriter); if (label) { @@ -5093,7 +5138,7 @@ namespace Slang writer.put(":\n"); } - dumpIR(module, slangWriter, IRDumpMode::Simplified); + dumpIR(module, options, inWriter); if (label) { @@ -5101,16 +5146,16 @@ namespace Slang } } - String getSlangIRAssembly(IRModule* module, IRDumpMode mode) + String getSlangIRAssembly(IRModule* module, const IRDumpOptions& options) { StringBuilder sb; - printSlangIRAssembly(sb, module, mode); + printSlangIRAssembly(sb, module, options); return sb; } - void dumpIR(IRModule* module, ISlangWriter* writer, IRDumpMode mode) + void dumpIR(IRModule* module, const IRDumpOptions& options, ISlangWriter* writer) { - String ir = getSlangIRAssembly(module, mode); + String ir = getSlangIRAssembly(module, options); writer->write(ir.getBuffer(), ir.getLength()); writer->flush(); } diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 56904b53e..ce3a5c29a 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1473,21 +1473,33 @@ struct IRModule : RefObject IRModuleInst* moduleInst; }; - /// How much detail to include in dumped IR. - /// - /// Used with the `dumpIR` functions to determine - /// whether a completely faithful, but verbose, IR - /// dump is produced, or something simplified for ease - /// or reading. - /// -enum class IRDumpMode + +struct IRDumpOptions { + typedef uint32_t Flags; + struct Flag + { + enum Enum : Flags + { + SourceLocations = 0x1, ///< If set will output source locations + }; + }; + + /// How much detail to include in dumped IR. + /// + /// Used with the `dumpIR` functions to determine + /// whether a completely faithful, but verbose, IR + /// dump is produced, or something simplified for ease + /// or reading. + /// + enum class Mode + { /// Produce a simplified IR dump. /// /// Simplified IR dumping will skip certain instructions /// and print them at their use sites instead, so that /// the overall dump is shorter and easier to read. - Simplified, + Simplified, /// Produce a detailed/accurate IR dump. /// @@ -1495,15 +1507,24 @@ enum class IRDumpMode /// the instructions that were present with no attempt /// to selectively skip them or give special formatting. /// - Detailed, + Detailed, + }; + + Mode mode = Mode::Simplified; + /// Flags to control output + /// Add Flag::SourceLocations to output source locations set on IR + Flags flags = 0; + + /// Must be set if source location output is desired + SourceManager* sourceManager = nullptr; }; -void printSlangIRAssembly(StringBuilder& builder, IRModule* module, IRDumpMode mode = IRDumpMode::Simplified); -String getSlangIRAssembly(IRModule* module, IRDumpMode mode = IRDumpMode::Simplified); +void printSlangIRAssembly(StringBuilder& builder, IRModule* module, const IRDumpOptions& options); +String getSlangIRAssembly(IRModule* module, const IRDumpOptions& options); -void dumpIR(IRModule* module, ISlangWriter* writer, IRDumpMode mode = IRDumpMode::Simplified); -void dumpIR(IRInst* globalVal, ISlangWriter* writer, IRDumpMode mode = IRDumpMode::Simplified); -void dumpIR(IRModule* module, ISlangWriter* slangWriter, char const* label); +void dumpIR(IRModule* module, const IRDumpOptions& options, ISlangWriter* writer); +void dumpIR(IRInst* globalVal, const IRDumpOptions& options, ISlangWriter* writer); +void dumpIR(IRModule* module, const IRDumpOptions& options, char const* label, ISlangWriter* writer); IRInst* createEmptyInst( IRModule* module, diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 9b1c4b1a6..ef4cb98ec 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -7043,7 +7043,16 @@ struct DeclLoweringVisitor : DeclVisitor { // `void`-returning function can get an implicit // return on exit of the body statement. - subContext->irBuilder->emitReturn(); + IRInst* returnInst = subContext->irBuilder->emitReturn(); + + if (BlockStmt* blockStmt = as(decl->body)) + { + returnInst->sourceLoc = blockStmt->closingSourceLoc; + } + else + { + returnInst->sourceLoc = SourceLoc(); + } } else { @@ -7938,7 +7947,11 @@ IRModule* generateIRForTranslationUnit( if(compileRequest->shouldDumpIR) { DiagnosticSinkWriter writer(compileRequest->getSink()); - dumpIR(module, &writer, "LOWER-TO-IR"); + + IRDumpOptions options; + options.sourceManager = compileRequest->getSourceManager(); + + dumpIR(module, options, "LOWER-TO-IR", &writer); } return module; diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 68d06714d..493e707c2 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -557,6 +557,16 @@ namespace Slang return false; } + bool AdvanceIf(Parser* parser, TokenType tokenType, Token* outToken) + { + if (parser->LookAheadToken(tokenType)) + { + *outToken = parser->ReadToken(); + return true; + } + return false; + } + // Consume a token and return true it if matches, otherwise false bool AdvanceIf(Parser* parser, char const* text) { @@ -590,6 +600,24 @@ namespace Slang return false; } + bool AdvanceIfMatch(Parser* parser, TokenType tokenType, Token* outToken) + { + // If we've run into a syntax error, but haven't recovered inside + // the block, then try to recover here. + if (parser->isRecovering) + { + TryRecoverBefore(parser, tokenType); + } + if (AdvanceIf(parser, tokenType, outToken)) + return true; + if (parser->tokenReader.peekTokenType() == TokenType::EndOfFile) + { + *outToken = parser->ReadToken(tokenType); + return true; + } + return false; + } + NodeBase* parseTypeDef(Parser* parser, void* /*userData*/) { TypeDefDecl* typeDefDecl = parser->astBuilder->create(); @@ -3925,7 +3953,9 @@ namespace Slang { FillPosition(blockStatement); } - while (!AdvanceIfMatch(this, TokenType::RBrace)) + + Token closingBraceToken; + while (!AdvanceIfMatch(this, TokenType::RBrace, &closingBraceToken)) { auto stmt = ParseStatement(); if(stmt) @@ -3952,6 +3982,9 @@ namespace Slang } PopScope(); + // Save the closing braces source loc + blockStatement->closingSourceLoc = closingBraceToken.loc; + if(!body) { body = astBuilder->create(); diff --git a/source/slang/slang-source-loc.cpp b/source/slang/slang-source-loc.cpp index f6b0afcc1..4b9e16b8e 100644 --- a/source/slang/slang-source-loc.cpp +++ b/source/slang/slang-source-loc.cpp @@ -21,6 +21,61 @@ const String PathInfo::getMostUniqueIdentity() const } } +bool PathInfo::operator==(const ThisType& rhs) const +{ + // They must be the same type + if (type != rhs.type) + { + return false; + } + + switch (type) + { + case Type::TokenPaste: + case Type::TypeParse: + case Type::Unknown: + case Type::CommandLine: + { + return true; + } + case Type::Normal: + { + return foundPath == rhs.foundPath && uniqueIdentity == rhs.uniqueIdentity; + } + case Type::FromString: + case Type::FoundPath: + { + // Only have a found path + return foundPath == rhs.foundPath; + } + default: break; + } + + return false; +} + +void PathInfo::appendDisplayName(StringBuilder& out) const +{ + switch (type) + { + case Type::TokenPaste: out << "[Token Paste]"; break; + case Type::TypeParse: out << "[Type Parse]"; break; + case Type::Unknown: out << "[Unknown]"; break; + case Type::CommandLine: out << "[Command Line]"; break; + case Type::Normal: + case Type::FromString: + case Type::FoundPath: + { + // TODO(JS): We might want to escape the path here if necessary + out.appendChar('"'); + out << foundPath; + out.appendChar('"'); + break; + } + default: break; + } +} + /* !!!!!!!!!!!!!!!!!!!!!!!!! SourceView !!!!!!!!!!!!!!!!!!!!!!!!!!!! */ int SourceView::findEntryIndex(SourceLoc sourceLoc) const diff --git a/source/slang/slang-source-loc.h b/source/slang/slang-source-loc.h index 1e91a9d62..ef8b49c3b 100644 --- a/source/slang/slang-source-loc.h +++ b/source/slang/slang-source-loc.h @@ -40,6 +40,8 @@ interpretation for that lex/parse. struct PathInfo { + typedef PathInfo ThisType; + /// To be more rigorous about where a path comes from, the type identifies what a paths origin is enum class Type : uint8_t { @@ -59,9 +61,15 @@ struct PathInfo /// True if has a found path that has originated from a file (as opposed to string or some other origin) SLANG_FORCE_INLINE bool hasFileFoundPath() const { return (type == Type::Normal || type == Type::FoundPath) && foundPath.getLength() > 0; } + bool operator==(const ThisType& rhs) const; + bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } + /// Returns the 'most unique' identity for the path. If has a 'uniqueIdentity' returns that, else the foundPath, else "". const String getMostUniqueIdentity() const; + /// Append to out, how to display the path + void appendDisplayName(StringBuilder& out) const; + // So simplify construction. In normal usage it's safer to use make methods over constructing directly. static PathInfo makeUnknown() { return PathInfo { Type::Unknown, String(), String() }; } static PathInfo makeTokenPaste() { return PathInfo{ Type::TokenPaste, "token paste", String()}; } diff --git a/tests/compute/half-texture.slang.1.expected b/tests/compute/half-texture.slang.1.expected index 1e1a282fa..7dd96403f 100644 --- a/tests/compute/half-texture.slang.1.expected +++ b/tests/compute/half-texture.slang.1.expected @@ -39,8 +39,6 @@ void computeMain(vector dispatchThreadID_0 : SV_DISPATCHTHREADID) int index_0 = pos_0.x + pos_0.y * int(4); outputBuffer_0[(uint) index_0] = index_0; - -#line 18 return; } diff --git a/tests/diagnostics/local-line.slang b/tests/diagnostics/local-line.slang new file mode 100644 index 000000000..1cdeba7e4 --- /dev/null +++ b/tests/diagnostics/local-line.slang @@ -0,0 +1,45 @@ +//TEST:SIMPLE_LINE:-entry computeMain -target spirv -stage compute +//TEST:SIMPLE_LINE:-entry computeMain -target dxil -profile cs_6_0 +//TEST:SIMPLE_LINE:-entry computeMain -target dxbc -stage compute +//TEST:SIMPLE_LINE:-entry computeMain -target dll -stage compute +//TEST:SIMPLE_LINE:-entry computeMain -target ptx -stage compute + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer outputBuffer; + +int doThing(int a, int b) +{ + while (b >= 0) + { + a + += + a; + } + + return a; +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int a = dispatchThreadID.x; + int b = dispatchThreadID.y; + int c = dispatchThreadID.z; + int d = a + b * c; + int e = d + c / 2; + + for (int i = 0; i < b; ++i) + { + if (e > 10 && i & 2) + { + a += b; b -= c; c += c; d = d + e + a; e = a; + } + else + { + a = e; b = c + c; d += d + __SyntaxError(); e = doThing(e, dispatchThreadID.x); + + } + } + + outputBuffer[dispatchThreadID.x] = a + b + c + d + e; +} diff --git a/tests/diagnostics/local-line.slang.expected b/tests/diagnostics/local-line.slang.expected new file mode 100644 index 000000000..a2720097d --- /dev/null +++ b/tests/diagnostics/local-line.slang.expected @@ -0,0 +1 @@ +39 -- cgit v1.2.3