Add initial support for 'goto' statements.
ispc now supports goto, but only under uniform control flow--i.e. it must be possible for the compiler to statically determine that all program instances will follow the goto. An error is issued at compile time if a goto is used when this is not the case.
This commit is contained in:
9
ast.cpp
9
ast.cpp
@@ -91,6 +91,7 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
||||
ForStmt *fs;
|
||||
ForeachStmt *fes;
|
||||
ReturnStmt *rs;
|
||||
LabeledStmt *ls;
|
||||
StmtList *sl;
|
||||
PrintStmt *ps;
|
||||
AssertStmt *as;
|
||||
@@ -131,9 +132,12 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
||||
fes->stmts = (Stmt *)WalkAST(fes->stmts, preFunc, postFunc, data);
|
||||
}
|
||||
else if (dynamic_cast<BreakStmt *>(node) != NULL ||
|
||||
dynamic_cast<ContinueStmt *>(node) != NULL) {
|
||||
// nothing
|
||||
dynamic_cast<ContinueStmt *>(node) != NULL ||
|
||||
dynamic_cast<GotoStmt *>(node) != NULL) {
|
||||
// nothing
|
||||
}
|
||||
else if ((ls = dynamic_cast<LabeledStmt *>(node)) != NULL)
|
||||
ls->stmt = (Stmt *)WalkAST(ls->stmt, preFunc, postFunc, data);
|
||||
else if ((rs = dynamic_cast<ReturnStmt *>(node)) != NULL)
|
||||
rs->val = (Expr *)WalkAST(rs->val, preFunc, postFunc, data);
|
||||
else if ((sl = dynamic_cast<StmtList *>(node)) != NULL) {
|
||||
@@ -289,3 +293,4 @@ EstimateCost(ASTNode *root) {
|
||||
WalkAST(root, lCostCallback, NULL, &cost);
|
||||
return cost;
|
||||
}
|
||||
|
||||
|
||||
@@ -376,7 +376,7 @@ namespace {
|
||||
if (I.getType() == Type::getVoidTy(I.getContext()) || !I.hasOneUse() ||
|
||||
isa<TerminatorInst>(I) || isa<CallInst>(I) || isa<PHINode>(I) ||
|
||||
isa<LoadInst>(I) || isa<VAArgInst>(I) || isa<InsertElementInst>(I) ||
|
||||
isa<InsertValueInst>(I) || isa<ExtractValueInst>(I))
|
||||
isa<InsertValueInst>(I) || isa<ExtractValueInst>(I) || isa<SelectInst>(I))
|
||||
// Don't inline a load across a store or other bad things!
|
||||
return false;
|
||||
|
||||
|
||||
45
ctx.cpp
45
ctx.cpp
@@ -77,7 +77,7 @@ struct CFInfo {
|
||||
bool IsIf() { return type == If; }
|
||||
bool IsLoop() { return type == Loop; }
|
||||
bool IsForeach() { return type == Foreach; }
|
||||
bool IsVaryingType() { return !isUniform; }
|
||||
bool IsVarying() { return !isUniform; }
|
||||
bool IsUniform() { return isUniform; }
|
||||
|
||||
enum CFType { If, Loop, Foreach };
|
||||
@@ -157,9 +157,10 @@ CFInfo::GetForeach(llvm::BasicBlock *breakTarget,
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FunctionEmitContext::FunctionEmitContext(Function *func, Symbol *funSym,
|
||||
llvm::Function *llvmFunction,
|
||||
llvm::Function *lf,
|
||||
SourcePos firstStmtPos) {
|
||||
function = func;
|
||||
llvmFunction = lf;
|
||||
|
||||
/* Create a new basic block to store all of the allocas */
|
||||
allocaBlock = llvm::BasicBlock::Create(*g->ctx, "allocas", llvmFunction, 0);
|
||||
@@ -762,7 +763,7 @@ int
|
||||
FunctionEmitContext::VaryingCFDepth() const {
|
||||
int sum = 0;
|
||||
for (unsigned int i = 0; i < controlFlowInfo.size(); ++i)
|
||||
if (controlFlowInfo[i]->IsVaryingType())
|
||||
if (controlFlowInfo[i]->IsVarying())
|
||||
++sum;
|
||||
return sum;
|
||||
}
|
||||
@@ -777,6 +778,41 @@ FunctionEmitContext::InForeachLoop() const {
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
FunctionEmitContext::initLabelBBlocks(ASTNode *node, void *data) {
|
||||
LabeledStmt *ls = dynamic_cast<LabeledStmt *>(node);
|
||||
if (ls == NULL)
|
||||
return true;
|
||||
|
||||
FunctionEmitContext *ctx = (FunctionEmitContext *)data;
|
||||
|
||||
if (ctx->labelMap.find(ls->name) != ctx->labelMap.end())
|
||||
Error(ls->pos, "Multiple labels named \"%s\" in function.",
|
||||
ls->name.c_str());
|
||||
else {
|
||||
llvm::BasicBlock *bb = ctx->CreateBasicBlock(ls->name.c_str());
|
||||
ctx->labelMap[ls->name] = bb;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FunctionEmitContext::InitializeLabelMap(Stmt *code) {
|
||||
labelMap.erase(labelMap.begin(), labelMap.end());
|
||||
WalkAST(code, initLabelBBlocks, NULL, this);
|
||||
}
|
||||
|
||||
|
||||
llvm::BasicBlock *
|
||||
FunctionEmitContext::GetLabeledBasicBlock(const std::string &label) {
|
||||
if (labelMap.find(label) != labelMap.end())
|
||||
return labelMap[label];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FunctionEmitContext::CurrentLanesReturned(Expr *expr, bool doCoherenceCheck) {
|
||||
const Type *returnType = function->GetReturnType();
|
||||
@@ -920,8 +956,7 @@ FunctionEmitContext::GetStringPtr(const std::string &str) {
|
||||
|
||||
llvm::BasicBlock *
|
||||
FunctionEmitContext::CreateBasicBlock(const char *name) {
|
||||
llvm::Function *function = bblock->getParent();
|
||||
return llvm::BasicBlock::Create(*g->ctx, name, function);
|
||||
return llvm::BasicBlock::Create(*g->ctx, name, llvmFunction);
|
||||
}
|
||||
|
||||
|
||||
|
||||
17
ctx.h
17
ctx.h
@@ -39,6 +39,7 @@
|
||||
#define ISPC_CTX_H 1
|
||||
|
||||
#include "ispc.h"
|
||||
#include <map>
|
||||
#include <llvm/InstrTypes.h>
|
||||
#include <llvm/Instructions.h>
|
||||
#include <llvm/Analysis/DIBuilder.h>
|
||||
@@ -192,6 +193,15 @@ public:
|
||||
|
||||
bool InForeachLoop() const;
|
||||
|
||||
/** Step through the code and find label statements; create a basic
|
||||
block for each one, so that subsequent calls to
|
||||
GetLabeledBasicBlock() return the corresponding basic block. */
|
||||
void InitializeLabelMap(Stmt *code);
|
||||
|
||||
/** If there is a label in the function with the given name, return the
|
||||
new basic block that it starts. */
|
||||
llvm::BasicBlock *GetLabeledBasicBlock(const std::string &label);
|
||||
|
||||
/** Called to generate code for 'return' statement; value is the
|
||||
expression in the return statement (if non-NULL), and
|
||||
doCoherenceCheck indicates whether instructions should be generated
|
||||
@@ -446,6 +456,9 @@ private:
|
||||
/** Pointer to the Function for which we're currently generating code. */
|
||||
Function *function;
|
||||
|
||||
/** LLVM function representation for the current function. */
|
||||
llvm::Function *llvmFunction;
|
||||
|
||||
/** The basic block into which we add any alloca instructions that need
|
||||
to go at the very start of the function. */
|
||||
llvm::BasicBlock *allocaBlock;
|
||||
@@ -537,6 +550,10 @@ private:
|
||||
tasks launched from the current function. */
|
||||
llvm::Value *launchGroupHandlePtr;
|
||||
|
||||
std::map<std::string, llvm::BasicBlock *> labelMap;
|
||||
|
||||
static bool initLabelBBlocks(ASTNode *node, void *data);
|
||||
|
||||
llvm::Value *pointerVectorToVoidPointers(llvm::Value *value);
|
||||
static void addGSMetadata(llvm::Value *inst, SourcePos pos);
|
||||
bool ifsInLoopAllUniform() const;
|
||||
|
||||
@@ -100,6 +100,7 @@ Contents:
|
||||
|
||||
* `Conditional Statements: "if"`_
|
||||
* `Basic Iteration Statements: "for", "while", and "do"`_
|
||||
* `Unstructured Control Flow: "goto"`_
|
||||
* `"Coherent" Control Flow Statements: "cif" and Friends`_
|
||||
* `Parallel Iteration Statements: "foreach" and "foreach_tiled"`_
|
||||
* `Parallel Iteration with "programIndex" and "programCount"`_
|
||||
@@ -1184,7 +1185,8 @@ but are likely to be supported in future releases:
|
||||
``int64`` types
|
||||
* Character constants
|
||||
* String constants and arrays of characters as strings
|
||||
* ``switch`` and ``goto`` statements
|
||||
* ``switch`` statements
|
||||
* ``goto`` statements are partially supported (see `Unstructured Control Flow: "goto"`_)
|
||||
* ``union`` types
|
||||
* Bitfield members of ``struct`` types
|
||||
* Variable numbers of arguments to functions
|
||||
@@ -2005,6 +2007,37 @@ one of them executes a ``continue`` statement, other program instances
|
||||
executing code in the loop body that didn't execute the ``continue`` will
|
||||
be unaffected by it.
|
||||
|
||||
Unstructured Control Flow: "goto"
|
||||
---------------------------------
|
||||
|
||||
``goto`` statements are allowed in ``ispc`` programs under limited
|
||||
circumstances; specifically, only when the compiler can determine that if
|
||||
any program instance executes a ``goto`` statement, then all of the program
|
||||
instances will be running at that statement, such that all will follow the
|
||||
``goto``.
|
||||
|
||||
Put another way: it's illegal for there to be "varying" control flow
|
||||
statements in scopes that enclose a ``goto`` statement. An error is issued
|
||||
if a ``goto`` is used in this situation.
|
||||
|
||||
The syntax for adding labels to ``ispc`` programs and jumping to them with
|
||||
``goto`` is the same as in C. The following code shows a ``goto`` based
|
||||
equivalent of a ``for`` loop where the induction variable ``i`` goes from
|
||||
zero to ten.
|
||||
|
||||
::
|
||||
|
||||
uniform int i = 0;
|
||||
check:
|
||||
if (i > 10)
|
||||
goto done;
|
||||
// loop body
|
||||
++i;
|
||||
goto check;
|
||||
done:
|
||||
// ...
|
||||
|
||||
|
||||
"Coherent" Control Flow Statements: "cif" and Friends
|
||||
-----------------------------------------------------
|
||||
|
||||
|
||||
@@ -307,6 +307,12 @@ static FORCEINLINE uint32_t __movmsk(__vec16_i1 mask) {
|
||||
return mask.v;
|
||||
}
|
||||
|
||||
static FORCEINLINE __vec16_i1 __equal(__vec16_i1 a, __vec16_i1 b) {
|
||||
__vec16_i1 r;
|
||||
r.v = (a.v & b.v) | (~a.v & ~b.v);
|
||||
return r;
|
||||
}
|
||||
|
||||
static FORCEINLINE __vec16_i1 __and(__vec16_i1 a, __vec16_i1 b) {
|
||||
__vec16_i1 r;
|
||||
r.v = a.v & b.v;
|
||||
|
||||
@@ -228,6 +228,10 @@ static FORCEINLINE uint32_t __movmsk(__vec4_i1 mask) {
|
||||
return _mm_movemask_ps(mask.v);
|
||||
}
|
||||
|
||||
static FORCEINLINE __vec4_i1 __equal(__vec4_i1 a, __vec4_i1 b) {
|
||||
return _mm_cmpeq_epi32(_mm_castps_si128(a.v), _mm_castps_si128(b.v));
|
||||
}
|
||||
|
||||
static FORCEINLINE __vec4_i1 __and(__vec4_i1 a, __vec4_i1 b) {
|
||||
return _mm_and_ps(a.v, b.v);
|
||||
}
|
||||
|
||||
13
func.cpp
13
func.cpp
@@ -290,8 +290,10 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function,
|
||||
llvm::BasicBlock *bbAllOn = ctx->CreateBasicBlock("all_on");
|
||||
llvm::BasicBlock *bbNotAll = ctx->CreateBasicBlock("not_all_on");
|
||||
|
||||
ctx->BranchInst(bbAllOn, bbNotAll, allOn);
|
||||
// Set up basic blocks for goto targets
|
||||
ctx->InitializeLabelMap(code);
|
||||
|
||||
ctx->BranchInst(bbAllOn, bbNotAll, allOn);
|
||||
// all on: we've determined dynamically that the mask is all
|
||||
// on. Set the current mask to "all on" explicitly so that
|
||||
// codegen for this path can be improved with this knowledge in
|
||||
@@ -322,12 +324,19 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function,
|
||||
// above
|
||||
ctx->SetCurrentBasicBlock(bbSomeOn);
|
||||
ctx->SetFunctionMask(mask);
|
||||
|
||||
// Set up basic blocks for goto targets again; we want to have
|
||||
// one set of them for gotos in the 'all on' case, and a
|
||||
// distinct set for the 'mixed mask' case.
|
||||
ctx->InitializeLabelMap(code);
|
||||
|
||||
code->EmitCode(ctx);
|
||||
if (ctx->GetCurrentBasicBlock())
|
||||
ctx->ReturnInst();
|
||||
|
||||
}
|
||||
else
|
||||
// Set up basic blocks for goto targets
|
||||
ctx->InitializeLabelMap(code);
|
||||
// No check, just emit the code
|
||||
code->EmitCode(ctx);
|
||||
}
|
||||
|
||||
3
ispc.h
3
ispc.h
@@ -98,6 +98,8 @@ namespace llvm {
|
||||
#endif
|
||||
|
||||
class ArrayType;
|
||||
class AST;
|
||||
class ASTNode;
|
||||
class AtomicType;
|
||||
class FunctionEmitContext;
|
||||
class Expr;
|
||||
@@ -421,6 +423,7 @@ enum {
|
||||
COST_FUNPTR_UNIFORM = 12,
|
||||
COST_FUNPTR_VARYING = 24,
|
||||
COST_GATHER = 8,
|
||||
COST_GOTO = 4,
|
||||
COST_LOAD = 2,
|
||||
COST_REGULAR_BREAK_CONTINUE = 2,
|
||||
COST_RETURN = 4,
|
||||
|
||||
16
parse.yy
16
parse.yy
@@ -224,7 +224,7 @@ struct ForeachDimension {
|
||||
%type <declSpecs> declaration_specifiers
|
||||
|
||||
%type <stringVal> string_constant
|
||||
%type <constCharPtr> struct_or_union_name enum_identifier
|
||||
%type <constCharPtr> struct_or_union_name enum_identifier goto_identifier
|
||||
%type <intVal> int_constant soa_width_specifier
|
||||
|
||||
%type <foreachDimension> foreach_dimension_specifier
|
||||
@@ -1262,7 +1262,11 @@ statement
|
||||
;
|
||||
|
||||
labeled_statement
|
||||
: TOKEN_CASE constant_expression ':' statement
|
||||
: goto_identifier ':' statement
|
||||
{
|
||||
$$ = new LabeledStmt($1, $3, @1);
|
||||
}
|
||||
| TOKEN_CASE constant_expression ':' statement
|
||||
{ UNIMPLEMENTED; }
|
||||
| TOKEN_DEFAULT ':' statement
|
||||
{ UNIMPLEMENTED; }
|
||||
@@ -1433,9 +1437,13 @@ iteration_statement
|
||||
}
|
||||
;
|
||||
|
||||
goto_identifier
|
||||
: TOKEN_IDENTIFIER { $$ = yylval.stringVal->c_str(); }
|
||||
;
|
||||
|
||||
jump_statement
|
||||
: TOKEN_GOTO TOKEN_IDENTIFIER ';'
|
||||
{ UNIMPLEMENTED; }
|
||||
: TOKEN_GOTO goto_identifier ';'
|
||||
{ $$ = new GotoStmt($2, @1, @2); }
|
||||
| TOKEN_CONTINUE ';'
|
||||
{ $$ = new ContinueStmt(false, @1); }
|
||||
| TOKEN_BREAK ';'
|
||||
|
||||
127
stmt.cpp
127
stmt.cpp
@@ -494,6 +494,7 @@ lEmitIfStatements(FunctionEmitContext *ctx, Stmt *stmts, const char *trueOrFalse
|
||||
ctx->EndScope();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
IfStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
// First check all of the things that might happen due to errors
|
||||
@@ -1915,6 +1916,132 @@ ReturnStmt::Print(int indent) const {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// GotoStmt
|
||||
|
||||
GotoStmt::GotoStmt(const char *l, SourcePos gotoPos, SourcePos ip)
|
||||
: Stmt(gotoPos) {
|
||||
label = l;
|
||||
identifierPos = ip;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GotoStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
if (ctx->VaryingCFDepth() > 0) {
|
||||
Error(pos, "\"goto\" statements are only legal under \"uniform\" "
|
||||
"control flow.");
|
||||
return;
|
||||
}
|
||||
if (ctx->InForeachLoop()) {
|
||||
Error(pos, "\"goto\" statements are currently illegal inside "
|
||||
"\"foreach\" loops.");
|
||||
return;
|
||||
}
|
||||
|
||||
llvm::BasicBlock *bb = ctx->GetLabeledBasicBlock(label);
|
||||
if (bb == NULL) {
|
||||
// TODO: use the string distance stuff to suggest alternatives if
|
||||
// there are some with names close to the label name we have here..
|
||||
Error(identifierPos, "No label named \"%s\" found in current function.",
|
||||
label.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->BranchInst(bb);
|
||||
ctx->SetCurrentBasicBlock(NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GotoStmt::Print(int indent) const {
|
||||
printf("%*cGoto label \"%s\"\n", indent, ' ', label.c_str());
|
||||
}
|
||||
|
||||
|
||||
Stmt *
|
||||
GotoStmt::Optimize() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
Stmt *
|
||||
GotoStmt::TypeCheck() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
GotoStmt::EstimateCost() const {
|
||||
return COST_GOTO;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// LabeledStmt
|
||||
|
||||
LabeledStmt::LabeledStmt(const char *n, Stmt *s, SourcePos p)
|
||||
: Stmt(p) {
|
||||
name = n;
|
||||
stmt = s;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LabeledStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
llvm::BasicBlock *bblock = ctx->GetLabeledBasicBlock(name);
|
||||
assert(bblock != NULL);
|
||||
|
||||
// End the current basic block with a jump to our basic block and then
|
||||
// set things up for emission to continue there. Note that the current
|
||||
// basic block may validly be NULL going into this statement due to an
|
||||
// earlier goto that NULLed it out; that doesn't stop us from
|
||||
// re-establishing a current basic block starting at the label..
|
||||
if (ctx->GetCurrentBasicBlock() != NULL)
|
||||
ctx->BranchInst(bblock);
|
||||
ctx->SetCurrentBasicBlock(bblock);
|
||||
|
||||
if (stmt != NULL)
|
||||
stmt->EmitCode(ctx);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LabeledStmt::Print(int indent) const {
|
||||
printf("%*cLabel \"%s\"\n", indent, ' ', name.c_str());
|
||||
if (stmt != NULL)
|
||||
stmt->Print(indent);
|
||||
}
|
||||
|
||||
|
||||
Stmt *
|
||||
LabeledStmt::Optimize() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
Stmt *
|
||||
LabeledStmt::TypeCheck() {
|
||||
if (!isalpha(name[0]) || name[0] == '_') {
|
||||
Error(pos, "Label must start with either alphabetic character or '_'.");
|
||||
return NULL;
|
||||
}
|
||||
for (unsigned int i = 1; i < name.size(); ++i) {
|
||||
if (!isalnum(name[i]) && name[i] != '_') {
|
||||
Error(pos, "Character \"%c\" is illegal in labels.", name[i]);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
LabeledStmt::EstimateCost() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// StmtList
|
||||
|
||||
|
||||
32
stmt.h
32
stmt.h
@@ -282,6 +282,38 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class GotoStmt : public Stmt {
|
||||
public:
|
||||
GotoStmt(const char *label, SourcePos gotoPos, SourcePos idPos);
|
||||
|
||||
void EmitCode(FunctionEmitContext *ctx) const;
|
||||
void Print(int indent) const;
|
||||
|
||||
Stmt *Optimize();
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
std::string label;
|
||||
SourcePos identifierPos;
|
||||
};
|
||||
|
||||
|
||||
class LabeledStmt : public Stmt {
|
||||
public:
|
||||
LabeledStmt(const char *label, Stmt *stmt, SourcePos p);
|
||||
|
||||
void EmitCode(FunctionEmitContext *ctx) const;
|
||||
void Print(int indent) const;
|
||||
|
||||
Stmt *Optimize();
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
std::string name;
|
||||
Stmt *stmt;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Representation of a list of statements in the program.
|
||||
*/
|
||||
class StmtList : public Stmt {
|
||||
|
||||
17
tests/goto-1.ispc
Normal file
17
tests/goto-1.ispc
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
float a = aFOO[programIndex];
|
||||
float b = 0.; b = a;
|
||||
RET[programIndex] = a+b;
|
||||
goto skip;
|
||||
RET[programIndex] = 0;
|
||||
skip:
|
||||
;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 2 + 2*programIndex;
|
||||
}
|
||||
18
tests/goto-2.ispc
Normal file
18
tests/goto-2.ispc
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
float a = aFOO[programIndex];
|
||||
float b = 0.; b = a;
|
||||
RET[programIndex] = a+b;
|
||||
if (all(a != 0))
|
||||
goto skip;
|
||||
RET[programIndex] = 0;
|
||||
skip:
|
||||
;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 2 + 2*programIndex;
|
||||
}
|
||||
18
tests/goto-3.ispc
Normal file
18
tests/goto-3.ispc
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
float a = aFOO[programIndex];
|
||||
float b = 0.; b = a;
|
||||
RET[programIndex] = a+b;
|
||||
if (all(a == 0))
|
||||
goto skip;
|
||||
RET[programIndex] = 0;
|
||||
skip:
|
||||
;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
}
|
||||
19
tests/goto-4.ispc
Normal file
19
tests/goto-4.ispc
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
float a = aFOO[programIndex];
|
||||
float b = 0.; b = a;
|
||||
RET[programIndex] = 0;
|
||||
encore:
|
||||
++RET[programIndex];
|
||||
if (any(a != 0)) {
|
||||
a = max(a-1, 0);
|
||||
goto encore;
|
||||
}
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = programCount+1;
|
||||
}
|
||||
10
tests_errors/goto-1.ispc
Normal file
10
tests_errors/goto-1.ispc
Normal file
@@ -0,0 +1,10 @@
|
||||
// Multiple labels named "label" in function
|
||||
|
||||
void func(int x) {
|
||||
label:
|
||||
;
|
||||
label:
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
11
tests_errors/goto-2.ispc
Normal file
11
tests_errors/goto-2.ispc
Normal file
@@ -0,0 +1,11 @@
|
||||
// "goto" statements are only legal under "uniform" control flow
|
||||
|
||||
void func(int x) {
|
||||
if (x < 0)
|
||||
goto label;
|
||||
|
||||
label:
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
11
tests_errors/goto-3.ispc
Normal file
11
tests_errors/goto-3.ispc
Normal file
@@ -0,0 +1,11 @@
|
||||
// "goto" statements are only legal under "uniform" control flow
|
||||
|
||||
void func(int x) {
|
||||
cif (x < 0)
|
||||
goto label;
|
||||
|
||||
label:
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
10
tests_errors/goto-4.ispc
Normal file
10
tests_errors/goto-4.ispc
Normal file
@@ -0,0 +1,10 @@
|
||||
// "goto" statements are only legal under "uniform" control flow
|
||||
|
||||
void func(int x) {
|
||||
label:
|
||||
|
||||
for(int i =0 ;i<x;)
|
||||
goto label;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user