From 593a3f7f6b98458d2bea75a589e0cf72b5ac3263 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Fri, 15 May 2015 18:44:16 +0000 Subject: [PATCH] glslang -> SPV: improved support for do-while/continue. Contributed by David Neto (dneto@google.com). git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@31205 e7fa87d3-cd2b-0410-9028-fcbf551c1848 --- SPIRV/GlslangToSpv.cpp | 6 +- SPIRV/SpvBuilder.cpp | 57 ++++++++--- SPIRV/SpvBuilder.h | 47 +++++++-- Test/baseResults/spv.do-simple.vert.out | 62 ++++++++++++ Test/baseResults/spv.for-simple.vert.out | 63 ++++++++++++ .../spv.while-continue-break.vert.out | 99 +++++++++++++++++++ Test/baseResults/spv.while-simple.vert.out | 58 +++++++++++ Test/runtests | 10 ++ Test/spv.do-simple.vert | 7 ++ Test/spv.do-while-continue-break.vert | 20 ++++ Test/spv.for-continue-break.vert | 20 ++++ Test/spv.for-simple.vert | 8 ++ Test/spv.while-continue-break.vert | 20 ++++ Test/spv.while-simple.vert | 7 ++ Test/test-spirv-list | 9 ++ 15 files changed, 473 insertions(+), 20 deletions(-) create mode 100644 Test/baseResults/spv.do-simple.vert.out create mode 100644 Test/baseResults/spv.for-simple.vert.out create mode 100644 Test/baseResults/spv.while-continue-break.vert.out create mode 100644 Test/baseResults/spv.while-simple.vert.out create mode 100644 Test/spv.do-simple.vert create mode 100644 Test/spv.do-while-continue-break.vert create mode 100644 Test/spv.for-continue-break.vert create mode 100644 Test/spv.for-simple.vert create mode 100644 Test/spv.while-continue-break.vert create mode 100644 Test/spv.while-simple.vert create mode 100644 Test/test-spirv-list diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 79b1a18c..e2d75ca9 100644 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -1159,19 +1159,21 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn bool bodyOut = false; if (! node->testFirst()) { + builder.endLoopHeaderWithoutTest(); if (node->getBody()) { breakForLoop.push(true); node->getBody()->traverse(this); breakForLoop.pop(); } bodyOut = true; + builder.createBranchToLoopTest(); } if (node->getTest()) { node->getTest()->traverse(this); // the AST only contained the test computation, not the branch, we have to add it spv::Id condition = builder.accessChainLoad(TranslatePrecisionDecoration(node->getTest()->getType())); - builder.createLoopHeaderBranch(condition); + builder.createLoopTestBranch(condition); } if (! bodyOut && node->getBody()) { @@ -1208,7 +1210,7 @@ bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::T case glslang::EOpContinue: if (loopTerminal.top()) loopTerminal.top()->traverse(this); - builder.createLoopBackEdge(); + builder.createLoopContinue(); break; case glslang::EOpReturn: if (inMain) diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp index abfc832a..dbcd981d 100644 --- a/SPIRV/SpvBuilder.cpp +++ b/SPIRV/SpvBuilder.cpp @@ -1719,6 +1719,7 @@ void Builder::makeNewLoop() loop.function = &getBuildPoint()->getParent(); loop.header = new Block(getUniqueId(), *loop.function); loop.merge = new Block(getUniqueId(), *loop.function); + loop.test = NULL; loops.push(loop); @@ -1730,43 +1731,75 @@ void Builder::makeNewLoop() setBuildPoint(loop.header); } -void Builder::createLoopHeaderBranch(Id condition) +void Builder::createLoopTestBranch(Id condition) { - Loop loop = loops.top(); + Loop& loop = loops.top(); + // If loop.test exists, then we've already generated the LoopMerge + // for this loop. + if (!loop.test) + createMerge(OpLoopMerge, loop.merge, LoopControlMaskNone); + + // Branching to the "body" block will keep control inside + // the loop. Block* body = new Block(getUniqueId(), *loop.function); - createMerge(OpLoopMerge, loop.merge, LoopControlMaskNone); createConditionalBranch(condition, body, loop.merge); loop.function->addBlock(body); setBuildPoint(body); } -// Add a back-edge (e.g "continue") for the innermost loop that you're in -void Builder::createLoopBackEdge(bool implicit) +void Builder::endLoopHeaderWithoutTest() { - Loop loop = loops.top(); + Loop& loop = loops.top(); - // Just branch back, and set up a block for dead code if it's a user continue - createBranch(loop.header); - if (! implicit) - createAndSetNoPredecessorBlock("post-loop-continue"); + createMerge(OpLoopMerge, loop.merge, LoopControlMaskNone); + Block* body = new Block(getUniqueId(), *loop.function); + createBranch(body); + loop.function->addBlock(body); + setBuildPoint(body); + + assert(!loop.test); + loop.test = new Block(getUniqueId(), *loop.function); +} + +void Builder::createBranchToLoopTest() +{ + Loop& loop = loops.top(); + Block* testBlock = loop.test; + assert(testBlock); + createBranch(testBlock); + loop.function->addBlock(testBlock); + setBuildPoint(testBlock); +} + +void Builder::createLoopContinue() +{ + Loop& loop = loops.top(); + if (loop.test) + createBranch(loop.test); + else + createBranch(loop.header); + // Set up a block for dead code. + createAndSetNoPredecessorBlock("post-loop-continue"); } // Add an exit (e.g. "break") for the innermost loop that you're in void Builder::createLoopExit() { createBranch(loops.top().merge); + // Set up a block for dead code. createAndSetNoPredecessorBlock("post-loop-break"); } // Close the innermost loop void Builder::closeLoop() { + Loop& loop = loops.top(); + // Branch back to the top - createLoopBackEdge(true); + createBranch(loop.header); // Add the merge block and set the build point to it - Loop loop = loops.top(); loop.function->addBlock(loop.merge); setBuildPoint(loop.merge); diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h index b38d4a54..e4e81788 100644 --- a/SPIRV/SpvBuilder.h +++ b/SPIRV/SpvBuilder.h @@ -357,13 +357,33 @@ public: // Start the beginning of a new loop. void makeNewLoop(); - // Add the branch at the end of the loop header, and leave the build position - // in the first block of the body. - // 'condition' is true if should exit the loop - void createLoopHeaderBranch(Id condition); + // Add the branch for the loop test, based on the given condition. + // The true branch goes to the block that remains inside the loop, and + // the false branch goes to the loop's merge block. The builder insertion + // point will be placed at the start of the inside-the-loop block. + void createLoopTestBranch(Id condition); - // Add a back-edge (e.g "continue") for the innermost loop that you're in - void createLoopBackEdge(bool implicit=false); + // Finish generating the loop header block in the case where the loop test + // is at the bottom of the loop. It will include the LoopMerge instruction + // and a branch to the rest of the body. The loop header block must be + // separate from the rest of the body to make room for the the two kinds + // of *Merge instructions that might have to occur just before a branch: + // the loop header must have a LoopMerge as its second-last instruction, + // and the body might begin with a conditional branch, which must have its + // own SelectionMerge instruction. + // Also create the basic block that will contain the loop test, but don't + // insert it into the function yet. Any "continue" constructs in this loop + // will branch to the loop test block. The builder insertion point will be + // placed at the start of the body block. + void endLoopHeaderWithoutTest(); + + // Generate a branch to the loop test block. This can only be called if + // the loop test is at the bottom of the loop. The builder insertion point + // is left at the start of the test block. + void createBranchToLoopTest(); + + // Add a branch to the test of the current (innermost) loop. + void createLoopContinue(); // Add an exit (e.g. "break") for the innermost loop that you're in void createLoopExit(); @@ -507,8 +527,23 @@ protected: // Data that needs to be kept in order to properly handle loops. struct Loop { + // The header is the first block generated for the loop. + // It dominates all the blocks in the loop, i.e. it is always + // executed before any others. + // If the loop test is executed before the body (as in "while" and + // "for" loops), then the header begins with the test code. + // Otherwise, the loop is a "do-while" loop and the header contains the + // start of the body of the loop (if the body exists). Block* header; + // The merge block marks the end of the loop. Control is transferred + // to the merge block when either the loop test fails, or when a + // nested "break" is encountered. Block* merge; + // If not NULL, the test block is the basic block containing the loop + // test and the conditional branch back to the header or the merge + // block. This is created for "do-while" loops, and is the target of + // any "continue" constructs that might exist. + Block* test; Function* function; }; diff --git a/Test/baseResults/spv.do-simple.vert.out b/Test/baseResults/spv.do-simple.vert.out new file mode 100644 index 00000000..668d4c4e --- /dev/null +++ b/Test/baseResults/spv.do-simple.vert.out @@ -0,0 +1,62 @@ +spv.do-simple.vert + + + +Linked vertex stage: + + + +// Module Version 99 +// Generated by (magic number): 51a00bb +// Id's are bound by 26 + + Source GLSL 300 + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Vertex 4 + Name 4 "main" + Name 9 "i" + Name 24 "gl_VertexID" + Name 25 "gl_InstanceID" + Decorate 9(i) PrecisionHigh + Decorate 24(gl_VertexID) PrecisionHigh + Decorate 24(gl_VertexID) BuiltIn VertexId + Decorate 24(gl_VertexID) NoStaticUse + Decorate 25(gl_InstanceID) PrecisionHigh + Decorate 25(gl_InstanceID) BuiltIn InstanceId + Decorate 25(gl_InstanceID) NoStaticUse + 2: TypeVoid + 3: TypeFunction 2 + 7: TypeInt 32 1 + 8: TypePointer Function 7(int) + 10: 7(int) Constant 0 + 16: 7(int) Constant 1 + 19: 7(int) Constant 10 + 20: TypeBool + 23: TypePointer Input 7(int) + 24(gl_VertexID): 23(ptr) Variable Input +25(gl_InstanceID): 23(ptr) Variable Input + 4(main): 2 Function None 3 + 5: Label + 9(i): 8(ptr) Variable Function + Store 9(i) 10 + Branch 11 + 11: Label + LoopMerge 12 None + Branch 13 + 13: Label + 15: 7(int) Load 9(i) + 17: 7(int) IAdd 15 16 + Store 9(i) 17 + Branch 14 + 14: Label + 18: 7(int) Load 9(i) + 21: 20(bool) SLessThan 18 19 + BranchConditional 21 22 12 + 22: Label + Branch 11 + 12: Label + Branch 6 + 6: Label + Return + FunctionEnd diff --git a/Test/baseResults/spv.for-simple.vert.out b/Test/baseResults/spv.for-simple.vert.out new file mode 100644 index 00000000..6dc18247 --- /dev/null +++ b/Test/baseResults/spv.for-simple.vert.out @@ -0,0 +1,63 @@ +spv.for-simple.vert + + + +Linked vertex stage: + + + +// Module Version 99 +// Generated by (magic number): 51a00bb +// Id's are bound by 26 + + Source GLSL 300 + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Vertex 4 + Name 4 "main" + Name 9 "i" + Name 18 "j" + Name 24 "gl_VertexID" + Name 25 "gl_InstanceID" + Decorate 9(i) PrecisionHigh + Decorate 18(j) PrecisionHigh + Decorate 24(gl_VertexID) PrecisionHigh + Decorate 24(gl_VertexID) BuiltIn VertexId + Decorate 24(gl_VertexID) NoStaticUse + Decorate 25(gl_InstanceID) PrecisionHigh + Decorate 25(gl_InstanceID) BuiltIn InstanceId + Decorate 25(gl_InstanceID) NoStaticUse + 2: TypeVoid + 3: TypeFunction 2 + 7: TypeInt 32 1 + 8: TypePointer Function 7(int) + 10: 7(int) Constant 0 + 14: 7(int) Constant 10 + 15: TypeBool + 19: 7(int) Constant 12 + 21: 7(int) Constant 1 + 23: TypePointer Input 7(int) + 24(gl_VertexID): 23(ptr) Variable Input +25(gl_InstanceID): 23(ptr) Variable Input + 4(main): 2 Function None 3 + 5: Label + 9(i): 8(ptr) Variable Function + 18(j): 8(ptr) Variable Function + Store 9(i) 10 + Branch 11 + 11: Label + 13: 7(int) Load 9(i) + 16: 15(bool) SLessThan 13 14 + LoopMerge 12 None + BranchConditional 16 17 12 + 17: Label + Store 18(j) 19 + 20: 7(int) Load 9(i) + 22: 7(int) IAdd 20 21 + Store 9(i) 22 + Branch 11 + 12: Label + Branch 6 + 6: Label + Return + FunctionEnd diff --git a/Test/baseResults/spv.while-continue-break.vert.out b/Test/baseResults/spv.while-continue-break.vert.out new file mode 100644 index 00000000..f072ab7c --- /dev/null +++ b/Test/baseResults/spv.while-continue-break.vert.out @@ -0,0 +1,99 @@ +spv.while-continue-break.vert + + + +Linked vertex stage: + + + +// Module Version 99 +// Generated by (magic number): 51a00bb +// Id's are bound by 43 + + Source GLSL 300 + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Vertex 4 + Name 4 "main" + Name 9 "i" + Name 18 "A" + Name 26 "B" + Name 28 "C" + Name 38 "D" + Name 41 "gl_VertexID" + Name 42 "gl_InstanceID" + Decorate 9(i) PrecisionHigh + Decorate 18(A) PrecisionHigh + Decorate 26(B) PrecisionHigh + Decorate 28(C) PrecisionHigh + Decorate 38(D) PrecisionHigh + Decorate 41(gl_VertexID) PrecisionHigh + Decorate 41(gl_VertexID) BuiltIn VertexId + Decorate 41(gl_VertexID) NoStaticUse + Decorate 42(gl_InstanceID) PrecisionHigh + Decorate 42(gl_InstanceID) BuiltIn InstanceId + Decorate 42(gl_InstanceID) NoStaticUse + 2: TypeVoid + 3: TypeFunction 2 + 7: TypeInt 32 1 + 8: TypePointer Function 7(int) + 10: 7(int) Constant 0 + 14: 7(int) Constant 10 + 15: TypeBool + 19: 7(int) Constant 1 + 21: 7(int) Constant 2 + 30: 7(int) Constant 5 + 39: 7(int) Constant 3 + 40: TypePointer Input 7(int) + 41(gl_VertexID): 40(ptr) Variable Input +42(gl_InstanceID): 40(ptr) Variable Input + 4(main): 2 Function None 3 + 5: Label + 9(i): 8(ptr) Variable Function + 18(A): 8(ptr) Variable Function + 26(B): 8(ptr) Variable Function + 28(C): 8(ptr) Variable Function + 38(D): 8(ptr) Variable Function + Store 9(i) 10 + Branch 11 + 11: Label + 13: 7(int) Load 9(i) + 16: 15(bool) SLessThan 13 14 + LoopMerge 12 None + BranchConditional 16 17 12 + 17: Label + Store 18(A) 19 + 20: 7(int) Load 9(i) + 22: 7(int) SMod 20 21 + 23: 15(bool) IEqual 22 10 + SelectionMerge 25 None + BranchConditional 23 24 25 + 24: Label + Store 26(B) 21 + Branch 11 + 27: Label + Store 28(C) 21 + Branch 25 + 25: Label + 29: 7(int) Load 9(i) + 31: 7(int) SMod 29 30 + 32: 15(bool) IEqual 31 10 + SelectionMerge 34 None + BranchConditional 32 33 34 + 33: Label + Store 26(B) 21 + Branch 12 + 35: Label + Store 28(C) 21 + Branch 34 + 34: Label + 36: 7(int) Load 9(i) + 37: 7(int) IAdd 36 19 + Store 9(i) 37 + Branch 11 + 12: Label + Store 38(D) 39 + Branch 6 + 6: Label + Return + FunctionEnd diff --git a/Test/baseResults/spv.while-simple.vert.out b/Test/baseResults/spv.while-simple.vert.out new file mode 100644 index 00000000..584afab4 --- /dev/null +++ b/Test/baseResults/spv.while-simple.vert.out @@ -0,0 +1,58 @@ +spv.while-simple.vert + + + +Linked vertex stage: + + + +// Module Version 99 +// Generated by (magic number): 51a00bb +// Id's are bound by 24 + + Source GLSL 300 + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Vertex 4 + Name 4 "main" + Name 9 "i" + Name 22 "gl_VertexID" + Name 23 "gl_InstanceID" + Decorate 9(i) PrecisionHigh + Decorate 22(gl_VertexID) PrecisionHigh + Decorate 22(gl_VertexID) BuiltIn VertexId + Decorate 22(gl_VertexID) NoStaticUse + Decorate 23(gl_InstanceID) PrecisionHigh + Decorate 23(gl_InstanceID) BuiltIn InstanceId + Decorate 23(gl_InstanceID) NoStaticUse + 2: TypeVoid + 3: TypeFunction 2 + 7: TypeInt 32 1 + 8: TypePointer Function 7(int) + 10: 7(int) Constant 0 + 14: 7(int) Constant 10 + 15: TypeBool + 19: 7(int) Constant 1 + 21: TypePointer Input 7(int) + 22(gl_VertexID): 21(ptr) Variable Input +23(gl_InstanceID): 21(ptr) Variable Input + 4(main): 2 Function None 3 + 5: Label + 9(i): 8(ptr) Variable Function + Store 9(i) 10 + Branch 11 + 11: Label + 13: 7(int) Load 9(i) + 16: 15(bool) SLessThan 13 14 + LoopMerge 12 None + BranchConditional 16 17 12 + 17: Label + 18: 7(int) Load 9(i) + 20: 7(int) IAdd 18 19 + Store 9(i) 20 + Branch 11 + 12: Label + Branch 6 + 6: Label + Return + FunctionEnd diff --git a/Test/runtests b/Test/runtests index 73b15a7c..8a270ab9 100755 --- a/Test/runtests +++ b/Test/runtests @@ -36,6 +36,16 @@ if [ -a localtestlist ] done < localtestlist fi +# +# SPIR-V code generation tests +# +grep -v "^#" test-spirv-list | while read t; do + echo Running SPIR-V $t... + b=`basename $t` + $EXE -H $t > $TARGETDIR/$b.out + diff -b $BASEDIR/$b.out $TARGETDIR/$b.out +done + # # grouped shaders for bulk (faster) tests # diff --git a/Test/spv.do-simple.vert b/Test/spv.do-simple.vert new file mode 100644 index 00000000..a7eba1d2 --- /dev/null +++ b/Test/spv.do-simple.vert @@ -0,0 +1,7 @@ +#version 300 es +void main() { + int i = 0; + do { + i++; + } while(i<10); +} diff --git a/Test/spv.do-while-continue-break.vert b/Test/spv.do-while-continue-break.vert new file mode 100644 index 00000000..1aa4bbd4 --- /dev/null +++ b/Test/spv.do-while-continue-break.vert @@ -0,0 +1,20 @@ +#version 300 es +void main() { + int i = 0; + int A, B, C, D, E, F, G; + do { + A = 0; + if (i == 2) { + B = 1; + continue; + C = 2; + } + if (i == 5) { + D = 3; + break; + E = 42; + } + F = 99; + } while (++i < 19); + G = 12; +} diff --git a/Test/spv.for-continue-break.vert b/Test/spv.for-continue-break.vert new file mode 100644 index 00000000..9044f391 --- /dev/null +++ b/Test/spv.for-continue-break.vert @@ -0,0 +1,20 @@ +#version 300 es +void main() { + int i; + int A, B, C, D, E, F, G; + for (i=0; i < 10 ; i++) { + A = 1; + if (i%2 ==0) { + B = 1; + continue; + C = 1; + } + if (i%3 == 0) { + D = 1; + break; + E = 1; + } + F = 12; + } + G = 99; +} diff --git a/Test/spv.for-simple.vert b/Test/spv.for-simple.vert new file mode 100644 index 00000000..7cba22ef --- /dev/null +++ b/Test/spv.for-simple.vert @@ -0,0 +1,8 @@ +#version 300 es +void main() { + int i; + int j; + for (i=0; i < 10 ; i++) { + j = 12; + } +} diff --git a/Test/spv.while-continue-break.vert b/Test/spv.while-continue-break.vert new file mode 100644 index 00000000..751e265c --- /dev/null +++ b/Test/spv.while-continue-break.vert @@ -0,0 +1,20 @@ +#version 300 es +void main() { + int i = 0; + int A, B, C, D; + while (i<10) { + A = 1; + if (i%2 == 0) { + B = 2; + continue; + C = 2; + } + if (i%5 == 0) { + B = 2; + break; + C = 2; + } + i++; + } + D = 3; +} diff --git a/Test/spv.while-simple.vert b/Test/spv.while-simple.vert new file mode 100644 index 00000000..701dc102 --- /dev/null +++ b/Test/spv.while-simple.vert @@ -0,0 +1,7 @@ +#version 300 es +void main() { + int i = 0; + while (i<10) { + i++; + } +} diff --git a/Test/test-spirv-list b/Test/test-spirv-list new file mode 100644 index 00000000..b5726393 --- /dev/null +++ b/Test/test-spirv-list @@ -0,0 +1,9 @@ +# Test looping constructs. +# No tests yet for making sure break and continue from a nested loop +# goes to the innermost target. +spv.do-simple.vert +spv.do-while-continue-break.vert +spv.for-continue-break.vert +spv.for-simple.vert +spv.while-continue-break.vert +spv.while-simple.vert