Add support for "switch" statements.
Switches with both uniform and varying "switch" expressions are supported. Switch statements with varying expressions and very large numbers of labels may not perform well; some issues to be filed shortly will track opportunities for improving these.
This commit is contained in:
11
ast.cpp
11
ast.cpp
@@ -90,6 +90,9 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
||||
DoStmt *dos;
|
||||
ForStmt *fs;
|
||||
ForeachStmt *fes;
|
||||
CaseStmt *cs;
|
||||
DefaultStmt *defs;
|
||||
SwitchStmt *ss;
|
||||
ReturnStmt *rs;
|
||||
LabeledStmt *ls;
|
||||
StmtList *sl;
|
||||
@@ -131,6 +134,14 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
||||
postFunc, data);
|
||||
fes->stmts = (Stmt *)WalkAST(fes->stmts, preFunc, postFunc, data);
|
||||
}
|
||||
else if ((cs = dynamic_cast<CaseStmt *>(node)) != NULL)
|
||||
cs->stmts = (Stmt *)WalkAST(cs->stmts, preFunc, postFunc, data);
|
||||
else if ((defs = dynamic_cast<DefaultStmt *>(node)) != NULL)
|
||||
defs->stmts = (Stmt *)WalkAST(defs->stmts, preFunc, postFunc, data);
|
||||
else if ((ss = dynamic_cast<SwitchStmt *>(node)) != NULL) {
|
||||
ss->expr = (Expr *)WalkAST(ss->expr, preFunc, postFunc, data);
|
||||
ss->stmts = (Stmt *)WalkAST(ss->stmts, preFunc, postFunc, data);
|
||||
}
|
||||
else if (dynamic_cast<BreakStmt *>(node) != NULL ||
|
||||
dynamic_cast<ContinueStmt *>(node) != NULL ||
|
||||
dynamic_cast<GotoStmt *>(node) != NULL) {
|
||||
|
||||
539
ctx.cpp
539
ctx.cpp
@@ -74,18 +74,33 @@ struct CFInfo {
|
||||
llvm::Value *savedContinueLanesPtr,
|
||||
llvm::Value *savedMask, llvm::Value *savedLoopMask);
|
||||
|
||||
static CFInfo *GetSwitch(bool isUniform, llvm::BasicBlock *breakTarget,
|
||||
llvm::BasicBlock *continueTarget,
|
||||
llvm::Value *savedBreakLanesPtr,
|
||||
llvm::Value *savedContinueLanesPtr,
|
||||
llvm::Value *savedMask, llvm::Value *savedLoopMask,
|
||||
llvm::Value *switchExpr,
|
||||
llvm::BasicBlock *bbDefault,
|
||||
const std::vector<std::pair<int, llvm::BasicBlock *> > *bbCases,
|
||||
const std::map<llvm::BasicBlock *, llvm::BasicBlock *> *bbNext);
|
||||
|
||||
bool IsIf() { return type == If; }
|
||||
bool IsLoop() { return type == Loop; }
|
||||
bool IsForeach() { return type == Foreach; }
|
||||
bool IsSwitch() { return type == Switch; }
|
||||
bool IsVarying() { return !isUniform; }
|
||||
bool IsUniform() { return isUniform; }
|
||||
|
||||
enum CFType { If, Loop, Foreach };
|
||||
enum CFType { If, Loop, Foreach, Switch };
|
||||
CFType type;
|
||||
bool isUniform;
|
||||
llvm::BasicBlock *savedBreakTarget, *savedContinueTarget;
|
||||
llvm::Value *savedBreakLanesPtr, *savedContinueLanesPtr;
|
||||
llvm::Value *savedMask, *savedLoopMask;
|
||||
llvm::Value *savedSwitchExpr;
|
||||
llvm::BasicBlock *savedDefaultBlock;
|
||||
const std::vector<std::pair<int, llvm::BasicBlock *> > *savedCaseBlocks;
|
||||
const std::map<llvm::BasicBlock *, llvm::BasicBlock *> *savedNextBlocks;
|
||||
|
||||
private:
|
||||
CFInfo(CFType t, bool uniformIf, llvm::Value *sm) {
|
||||
@@ -95,11 +110,17 @@ private:
|
||||
savedBreakTarget = savedContinueTarget = NULL;
|
||||
savedBreakLanesPtr = savedContinueLanesPtr = NULL;
|
||||
savedMask = savedLoopMask = sm;
|
||||
savedSwitchExpr = NULL;
|
||||
savedDefaultBlock = NULL;
|
||||
savedCaseBlocks = NULL;
|
||||
savedNextBlocks = NULL;
|
||||
}
|
||||
CFInfo(CFType t, bool iu, llvm::BasicBlock *bt, llvm::BasicBlock *ct,
|
||||
llvm::Value *sb, llvm::Value *sc, llvm::Value *sm,
|
||||
llvm::Value *lm) {
|
||||
Assert(t == Loop);
|
||||
llvm::Value *lm, llvm::Value *sse = NULL, llvm::BasicBlock *bbd = NULL,
|
||||
const std::vector<std::pair<int, llvm::BasicBlock *> > *bbc = NULL,
|
||||
const std::map<llvm::BasicBlock *, llvm::BasicBlock *> *bbn = NULL) {
|
||||
Assert(t == Loop || t == Switch);
|
||||
type = t;
|
||||
isUniform = iu;
|
||||
savedBreakTarget = bt;
|
||||
@@ -108,6 +129,10 @@ private:
|
||||
savedContinueLanesPtr = sc;
|
||||
savedMask = sm;
|
||||
savedLoopMask = lm;
|
||||
savedSwitchExpr = sse;
|
||||
savedDefaultBlock = bbd;
|
||||
savedCaseBlocks = bbc;
|
||||
savedNextBlocks = bbn;
|
||||
}
|
||||
CFInfo(CFType t, llvm::BasicBlock *bt, llvm::BasicBlock *ct,
|
||||
llvm::Value *sb, llvm::Value *sc, llvm::Value *sm,
|
||||
@@ -121,6 +146,10 @@ private:
|
||||
savedContinueLanesPtr = sc;
|
||||
savedMask = sm;
|
||||
savedLoopMask = lm;
|
||||
savedSwitchExpr = NULL;
|
||||
savedDefaultBlock = NULL;
|
||||
savedCaseBlocks = NULL;
|
||||
savedNextBlocks = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -154,6 +183,22 @@ CFInfo::GetForeach(llvm::BasicBlock *breakTarget,
|
||||
savedMask, savedForeachMask);
|
||||
}
|
||||
|
||||
|
||||
CFInfo *
|
||||
CFInfo::GetSwitch(bool isUniform, llvm::BasicBlock *breakTarget,
|
||||
llvm::BasicBlock *continueTarget,
|
||||
llvm::Value *savedBreakLanesPtr,
|
||||
llvm::Value *savedContinueLanesPtr, llvm::Value *savedMask,
|
||||
llvm::Value *savedLoopMask, llvm::Value *savedSwitchExpr,
|
||||
llvm::BasicBlock *savedDefaultBlock,
|
||||
const std::vector<std::pair<int, llvm::BasicBlock *> > *savedCases,
|
||||
const std::map<llvm::BasicBlock *, llvm::BasicBlock *> *savedNext) {
|
||||
return new CFInfo(Switch, isUniform, breakTarget, continueTarget,
|
||||
savedBreakLanesPtr, savedContinueLanesPtr,
|
||||
savedMask, savedLoopMask, savedSwitchExpr, savedDefaultBlock,
|
||||
savedCases, savedNext);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FunctionEmitContext::FunctionEmitContext(Function *func, Symbol *funSym,
|
||||
@@ -182,6 +227,11 @@ FunctionEmitContext::FunctionEmitContext(Function *func, Symbol *funSym,
|
||||
breakLanesPtr = continueLanesPtr = NULL;
|
||||
breakTarget = continueTarget = NULL;
|
||||
|
||||
switchExpr = NULL;
|
||||
caseBlocks = NULL;
|
||||
defaultBlock = NULL;
|
||||
nextBlocks = NULL;
|
||||
|
||||
returnedLanesPtr = AllocaInst(LLVMTypes::MaskType, "returned_lanes_memory");
|
||||
StoreInst(LLVMMaskAllOff, returnedLanesPtr);
|
||||
|
||||
@@ -422,51 +472,61 @@ FunctionEmitContext::StartVaryingIf(llvm::Value *oldMask) {
|
||||
|
||||
void
|
||||
FunctionEmitContext::EndIf() {
|
||||
CFInfo *ci = popCFState();
|
||||
// Make sure we match up with a Start{Uniform,Varying}If().
|
||||
Assert(controlFlowInfo.size() > 0 && controlFlowInfo.back()->IsIf());
|
||||
CFInfo *ci = controlFlowInfo.back();
|
||||
controlFlowInfo.pop_back();
|
||||
Assert(ci->IsIf());
|
||||
|
||||
// 'uniform' ifs don't change the mask so we only need to restore the
|
||||
// mask going into the if for 'varying' if statements
|
||||
if (!ci->IsUniform() && bblock != NULL) {
|
||||
// We can't just restore the mask as it was going into the 'if'
|
||||
// statement. First we have to take into account any program
|
||||
// instances that have executed 'return' statements; the restored
|
||||
// mask must be off for those lanes.
|
||||
restoreMaskGivenReturns(ci->savedMask);
|
||||
if (ci->IsUniform() || bblock == NULL)
|
||||
return;
|
||||
|
||||
// If the 'if' statement is inside a loop with a 'varying'
|
||||
// consdition, we also need to account for any break or continue
|
||||
// statements that executed inside the 'if' statmeent; we also must
|
||||
// leave the lane masks for the program instances that ran those
|
||||
// off after we restore the mask after the 'if'. The code below
|
||||
// ends up being optimized out in the case that there were no break
|
||||
// or continue statements (and breakLanesPtr and continueLanesPtr
|
||||
// have their initial 'all off' values), so we don't need to check
|
||||
// for that here.
|
||||
if (continueLanesPtr != NULL) {
|
||||
// We want to compute:
|
||||
// newMask = (oldMask & ~(breakLanes | continueLanes))
|
||||
llvm::Value *oldMask = GetInternalMask();
|
||||
llvm::Value *continueLanes = LoadInst(continueLanesPtr,
|
||||
"continue_lanes");
|
||||
llvm::Value *bcLanes = continueLanes;
|
||||
// We can't just restore the mask as it was going into the 'if'
|
||||
// statement. First we have to take into account any program
|
||||
// instances that have executed 'return' statements; the restored
|
||||
// mask must be off for those lanes.
|
||||
restoreMaskGivenReturns(ci->savedMask);
|
||||
|
||||
if (breakLanesPtr != NULL) {
|
||||
// breakLanesPtr will be NULL if we're inside a 'foreach' loop
|
||||
llvm::Value *breakLanes = LoadInst(breakLanesPtr, "break_lanes");
|
||||
bcLanes = BinaryOperator(llvm::Instruction::Or, breakLanes,
|
||||
continueLanes, "break|continue_lanes");
|
||||
}
|
||||
// If the 'if' statement is inside a loop with a 'varying'
|
||||
// condition, we also need to account for any break or continue
|
||||
// statements that executed inside the 'if' statmeent; we also must
|
||||
// leave the lane masks for the program instances that ran those
|
||||
// off after we restore the mask after the 'if'. The code below
|
||||
// ends up being optimized out in the case that there were no break
|
||||
// or continue statements (and breakLanesPtr and continueLanesPtr
|
||||
// have their initial 'all off' values), so we don't need to check
|
||||
// for that here.
|
||||
//
|
||||
// There are three general cases to deal with here:
|
||||
// - Loops: both break and continue are allowed, and thus the corresponding
|
||||
// lane mask pointers are non-NULL
|
||||
// - Foreach: only continueLanesPtr may be non-NULL
|
||||
// - Switch: only breakLanesPtr may be non-NULL
|
||||
if (continueLanesPtr != NULL || breakLanesPtr != NULL) {
|
||||
// We want to compute:
|
||||
// newMask = (oldMask & ~(breakLanes | continueLanes)),
|
||||
// treading breakLanes or continueLanes as "all off" if the
|
||||
// corresponding pointer is NULL.
|
||||
llvm::Value *bcLanes = NULL;
|
||||
|
||||
llvm::Value *notBreakOrContinue =
|
||||
NotOperator(bcLanes, "!(break|continue)_lanes");
|
||||
llvm::Value *newMask =
|
||||
BinaryOperator(llvm::Instruction::And, oldMask,
|
||||
notBreakOrContinue, "new_mask");
|
||||
SetInternalMask(newMask);
|
||||
if (continueLanesPtr != NULL)
|
||||
bcLanes = LoadInst(continueLanesPtr, "continue_lanes");
|
||||
else
|
||||
bcLanes = LLVMMaskAllOff;
|
||||
|
||||
if (breakLanesPtr != NULL) {
|
||||
llvm::Value *breakLanes = LoadInst(breakLanesPtr, "break_lanes");
|
||||
bcLanes = BinaryOperator(llvm::Instruction::Or, bcLanes,
|
||||
breakLanes, "|break_lanes");
|
||||
}
|
||||
|
||||
llvm::Value *notBreakOrContinue =
|
||||
NotOperator(bcLanes, "!(break|continue)_lanes");
|
||||
llvm::Value *oldMask = GetInternalMask();
|
||||
llvm::Value *newMask =
|
||||
BinaryOperator(llvm::Instruction::And, oldMask,
|
||||
notBreakOrContinue, "new_mask");
|
||||
SetInternalMask(newMask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,17 +562,8 @@ FunctionEmitContext::StartLoop(llvm::BasicBlock *bt, llvm::BasicBlock *ct,
|
||||
|
||||
void
|
||||
FunctionEmitContext::EndLoop() {
|
||||
Assert(controlFlowInfo.size() && controlFlowInfo.back()->IsLoop());
|
||||
CFInfo *ci = controlFlowInfo.back();
|
||||
controlFlowInfo.pop_back();
|
||||
|
||||
// Restore the break/continue state information to what it was before
|
||||
// we went into this loop.
|
||||
breakTarget = ci->savedBreakTarget;
|
||||
continueTarget = ci->savedContinueTarget;
|
||||
breakLanesPtr = ci->savedBreakLanesPtr;
|
||||
continueLanesPtr = ci->savedContinueLanesPtr;
|
||||
loopMask = ci->savedLoopMask;
|
||||
CFInfo *ci = popCFState();
|
||||
Assert(ci->IsLoop());
|
||||
|
||||
if (!ci->IsUniform())
|
||||
// If the loop had a 'uniform' test, then it didn't make any
|
||||
@@ -545,17 +596,8 @@ FunctionEmitContext::StartForeach(llvm::BasicBlock *ct) {
|
||||
|
||||
void
|
||||
FunctionEmitContext::EndForeach() {
|
||||
Assert(controlFlowInfo.size() && controlFlowInfo.back()->IsForeach());
|
||||
CFInfo *ci = controlFlowInfo.back();
|
||||
controlFlowInfo.pop_back();
|
||||
|
||||
// Restore the break/continue state information to what it was before
|
||||
// we went into this loop.
|
||||
breakTarget = ci->savedBreakTarget;
|
||||
continueTarget = ci->savedContinueTarget;
|
||||
breakLanesPtr = ci->savedBreakLanesPtr;
|
||||
continueLanesPtr = ci->savedContinueLanesPtr;
|
||||
loopMask = ci->savedLoopMask;
|
||||
CFInfo *ci = popCFState();
|
||||
Assert(ci->IsForeach());
|
||||
}
|
||||
|
||||
|
||||
@@ -576,28 +618,85 @@ FunctionEmitContext::restoreMaskGivenReturns(llvm::Value *oldMask) {
|
||||
}
|
||||
|
||||
|
||||
/** Returns "true" if the first enclosing "switch" statement (if any) has a
|
||||
uniform condition. It is legal to call this outside of the scope of an
|
||||
enclosing switch. */
|
||||
bool
|
||||
FunctionEmitContext::inUniformSwitch() const {
|
||||
// Go backwards through controlFlowInfo, since we add new nested scopes
|
||||
// to the back.
|
||||
int i = controlFlowInfo.size() - 1;
|
||||
while (i >= 0 && controlFlowInfo[i]->type != CFInfo::Switch)
|
||||
--i;
|
||||
if (i == -1)
|
||||
return false;
|
||||
return controlFlowInfo[i]->IsUniform();
|
||||
}
|
||||
|
||||
|
||||
/** Along the lines of inUniformSwitch(), this returns "true" if the first
|
||||
enclosing switch has a varying condition. Note that both
|
||||
inUniformSwitch() and inVaryingSwitch() may return false, which
|
||||
indicates that we're not currently inside a switch's scope. */
|
||||
bool
|
||||
FunctionEmitContext::inVaryingSwitch() const {
|
||||
// Go backwards through controlFlowInfo, since we add new nested scopes
|
||||
// to the back.
|
||||
int i = controlFlowInfo.size() - 1;
|
||||
while (i >= 0 && controlFlowInfo[i]->type != CFInfo::Switch)
|
||||
--i;
|
||||
if (i == -1)
|
||||
return false;
|
||||
return controlFlowInfo[i]->IsVarying();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FunctionEmitContext::Break(bool doCoherenceCheck) {
|
||||
Assert(controlFlowInfo.size() > 0);
|
||||
if (breakTarget == NULL) {
|
||||
Error(currentPos, "\"break\" statement is illegal outside of "
|
||||
"for/while/do loops.");
|
||||
"for/while/do loops and \"switch\" statements.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bblock == NULL)
|
||||
return;
|
||||
|
||||
if (inUniformSwitch()) {
|
||||
// FIXME: Currently, if there are any "break" statements under
|
||||
// varying "if" statements inside a switch with a uniform
|
||||
// condition, then the SwitchStmt code promotes the condition to
|
||||
// varying; hence this assert. However, we can do better than
|
||||
// that--see issue XXX. When that issue is fixed, this assert will
|
||||
// be wrong, and should be a second test in the if() statement
|
||||
// above.
|
||||
Assert(ifsInCFAllUniform(CFInfo::Switch));
|
||||
|
||||
// We know that all program instances are executing the break, so
|
||||
// just jump to the block immediately after the switch.
|
||||
Assert(breakTarget != NULL);
|
||||
BranchInst(breakTarget);
|
||||
bblock = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// If all of the enclosing 'if' tests in the loop have uniform control
|
||||
// flow or if we can tell that the mask is all on, then we can just
|
||||
// jump to the break location.
|
||||
if (ifsInLoopAllUniform() || GetInternalMask() == LLVMMaskAllOn) {
|
||||
if (!inVaryingSwitch() && (ifsInCFAllUniform(CFInfo::Loop) ||
|
||||
GetInternalMask() == LLVMMaskAllOn)) {
|
||||
BranchInst(breakTarget);
|
||||
if (ifsInLoopAllUniform() && doCoherenceCheck)
|
||||
Warning(currentPos, "Coherent break statement not necessary in fully uniform "
|
||||
"control flow.");
|
||||
if (ifsInCFAllUniform(CFInfo::Loop) && doCoherenceCheck)
|
||||
Warning(currentPos, "Coherent break statement not necessary in "
|
||||
"fully uniform control flow.");
|
||||
// Set bblock to NULL since the jump has terminated the basic block
|
||||
bblock = NULL;
|
||||
}
|
||||
else {
|
||||
// Otherwise we need to update the mask of the lanes that have
|
||||
// executed a 'break' statement:
|
||||
// Varying switch, or a loop with varying 'if's above the break.
|
||||
// In these cases, we need to update the mask of the lanes that
|
||||
// have executed a 'break' statement:
|
||||
// breakLanes = breakLanes | mask
|
||||
Assert(breakLanesPtr != NULL);
|
||||
llvm::Value *mask = GetInternalMask();
|
||||
@@ -613,7 +712,7 @@ FunctionEmitContext::Break(bool doCoherenceCheck) {
|
||||
// an 'if' statement and restore the mask then.
|
||||
SetInternalMask(LLVMMaskAllOff);
|
||||
|
||||
if (doCoherenceCheck)
|
||||
if (doCoherenceCheck && !inVaryingSwitch())
|
||||
// If the user has indicated that this is a 'coherent' break
|
||||
// statement, then check to see if the mask is all off. If so,
|
||||
// we have to conservatively jump to the continueTarget, not
|
||||
@@ -635,12 +734,12 @@ FunctionEmitContext::Continue(bool doCoherenceCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ifsInLoopAllUniform() || GetInternalMask() == LLVMMaskAllOn) {
|
||||
if (ifsInCFAllUniform(CFInfo::Loop) || GetInternalMask() == LLVMMaskAllOn) {
|
||||
// Similarly to 'break' statements, we can immediately jump to the
|
||||
// continue target if we're only in 'uniform' control flow within
|
||||
// loop or if we can tell that the mask is all on.
|
||||
AddInstrumentationPoint("continue: uniform CF, jumped");
|
||||
if (ifsInLoopAllUniform() && doCoherenceCheck)
|
||||
if (ifsInCFAllUniform(CFInfo::Loop) && doCoherenceCheck)
|
||||
Warning(currentPos, "Coherent continue statement not necessary in "
|
||||
"fully uniform control flow.");
|
||||
BranchInst(continueTarget);
|
||||
@@ -653,8 +752,9 @@ FunctionEmitContext::Continue(bool doCoherenceCheck) {
|
||||
llvm::Value *mask = GetInternalMask();
|
||||
llvm::Value *continueMask =
|
||||
LoadInst(continueLanesPtr, "continue_mask");
|
||||
llvm::Value *newMask = BinaryOperator(llvm::Instruction::Or,
|
||||
mask, continueMask, "mask|continueMask");
|
||||
llvm::Value *newMask =
|
||||
BinaryOperator(llvm::Instruction::Or, mask, continueMask,
|
||||
"mask|continueMask");
|
||||
StoreInst(newMask, continueLanesPtr);
|
||||
|
||||
// And set the current mask to be all off in case there are any
|
||||
@@ -671,22 +771,23 @@ FunctionEmitContext::Continue(bool doCoherenceCheck) {
|
||||
|
||||
|
||||
/** This function checks to see if all of the 'if' statements (if any)
|
||||
between the current scope and the first enclosing loop have 'uniform'
|
||||
tests.
|
||||
between the current scope and the first enclosing loop/switch of given
|
||||
control flow type have 'uniform' tests.
|
||||
*/
|
||||
bool
|
||||
FunctionEmitContext::ifsInLoopAllUniform() const {
|
||||
FunctionEmitContext::ifsInCFAllUniform(int type) const {
|
||||
Assert(controlFlowInfo.size() > 0);
|
||||
// Go backwards through controlFlowInfo, since we add new nested scopes
|
||||
// to the back. Stop once we come to the first enclosing loop.
|
||||
// to the back. Stop once we come to the first enclosing control flow
|
||||
// structure of the desired type.
|
||||
int i = controlFlowInfo.size() - 1;
|
||||
while (i >= 0 && controlFlowInfo[i]->type != CFInfo::Loop) {
|
||||
while (i >= 0 && controlFlowInfo[i]->type != type) {
|
||||
if (controlFlowInfo[i]->isUniform == false)
|
||||
// Found a scope due to an 'if' statement with a varying test
|
||||
return false;
|
||||
--i;
|
||||
}
|
||||
Assert(i >= 0); // else we didn't find a loop!
|
||||
Assert(i >= 0); // else we didn't find the expected control flow type!
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -759,6 +860,243 @@ FunctionEmitContext::RestoreContinuedLanes() {
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FunctionEmitContext::StartSwitch(bool isUniform, llvm::BasicBlock *bbBreak) {
|
||||
llvm::Value *oldMask = GetInternalMask();
|
||||
controlFlowInfo.push_back(CFInfo::GetSwitch(isUniform, breakTarget,
|
||||
continueTarget, breakLanesPtr,
|
||||
continueLanesPtr, oldMask,
|
||||
loopMask, switchExpr, defaultBlock,
|
||||
caseBlocks, nextBlocks));
|
||||
|
||||
breakLanesPtr = AllocaInst(LLVMTypes::MaskType, "break_lanes_memory");
|
||||
StoreInst(LLVMMaskAllOff, breakLanesPtr);
|
||||
breakTarget = bbBreak;
|
||||
|
||||
continueLanesPtr = NULL;
|
||||
continueTarget = NULL;
|
||||
loopMask = NULL;
|
||||
|
||||
// These will be set by the SwitchInst() method
|
||||
switchExpr = NULL;
|
||||
defaultBlock = NULL;
|
||||
caseBlocks = NULL;
|
||||
nextBlocks = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FunctionEmitContext::EndSwitch() {
|
||||
Assert(bblock != NULL);
|
||||
|
||||
CFInfo *ci = popCFState();
|
||||
if (ci->IsVarying() && bblock != NULL)
|
||||
restoreMaskGivenReturns(ci->savedMask);
|
||||
}
|
||||
|
||||
|
||||
/** Emit code to check for an "all off" mask before the code for a
|
||||
case or default label in a "switch" statement.
|
||||
*/
|
||||
void
|
||||
FunctionEmitContext::addSwitchMaskCheck(llvm::Value *mask) {
|
||||
llvm::Value *allOff = None(mask);
|
||||
llvm::BasicBlock *bbSome = CreateBasicBlock("case_default_on");
|
||||
|
||||
// Find the basic block for the case or default label immediately after
|
||||
// the current one in the switch statement--that's where we want to
|
||||
// jump if the mask is all off at this label.
|
||||
Assert(nextBlocks->find(bblock) != nextBlocks->end());
|
||||
llvm::BasicBlock *bbNext = nextBlocks->find(bblock)->second;
|
||||
|
||||
// Jump to the next one of the mask is all off; otherwise jump to the
|
||||
// newly created block that will hold the actual code for this label.
|
||||
BranchInst(bbNext, bbSome, allOff);
|
||||
SetCurrentBasicBlock(bbSome);
|
||||
}
|
||||
|
||||
|
||||
/** Returns the execution mask at entry to the first enclosing "switch"
|
||||
statement. */
|
||||
llvm::Value *
|
||||
FunctionEmitContext::getMaskAtSwitchEntry() {
|
||||
Assert(controlFlowInfo.size() > 0);
|
||||
int i = controlFlowInfo.size() - 1;
|
||||
while (i >= 0 && controlFlowInfo[i]->type != CFInfo::Switch)
|
||||
--i;
|
||||
Assert(i != -1);
|
||||
return controlFlowInfo[i]->savedMask;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FunctionEmitContext::EmitDefaultLabel(bool checkMask, SourcePos pos) {
|
||||
if (!inUniformSwitch() && !inVaryingSwitch()) {
|
||||
Error(pos, "\"default\" label illegal outside of \"switch\" "
|
||||
"statement.");
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's a default label in the switch, a basic block for it
|
||||
// should have been provided in the previous call to SwitchInst().
|
||||
Assert(defaultBlock != NULL);
|
||||
|
||||
if (bblock != NULL)
|
||||
// The previous case in the switch fell through, or we're in a
|
||||
// varying switch; terminate the current block with a jump to the
|
||||
// block for the code for the default label.
|
||||
BranchInst(defaultBlock);
|
||||
SetCurrentBasicBlock(defaultBlock);
|
||||
|
||||
if (inUniformSwitch())
|
||||
// Nothing more to do for the uniform case; return back to the
|
||||
// caller, which will then emit the code for the default case.
|
||||
return;
|
||||
|
||||
// For a varying switch, we need to update the execution mask.
|
||||
//
|
||||
// First, compute the mask that corresponds to which program instances
|
||||
// should execute the "default" code; this corresponds to the set of
|
||||
// program instances that don't match any of the case statements.
|
||||
// Therefore, we generate code that compares the value of the switch
|
||||
// expression to the value associated with each of the "case"
|
||||
// statements such that the surviving lanes didn't match any of them.
|
||||
llvm::Value *matchesDefault = getMaskAtSwitchEntry();
|
||||
for (int i = 0; i < (int)caseBlocks->size(); ++i) {
|
||||
int value = (*caseBlocks)[i].first;
|
||||
llvm::Value *valueVec = (switchExpr->getType() == LLVMTypes::Int32VectorType) ?
|
||||
LLVMInt32Vector(value) : LLVMInt64Vector(value);
|
||||
// TODO: for AVX2 at least, the following generates better code
|
||||
// than doing ICMP_NE and skipping the NotOperator() below; file a
|
||||
// LLVM bug?
|
||||
llvm::Value *matchesCaseValue =
|
||||
CmpInst(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_EQ, switchExpr,
|
||||
valueVec, "cmp_case_value");
|
||||
matchesCaseValue = I1VecToBoolVec(matchesCaseValue);
|
||||
|
||||
llvm::Value *notMatchesCaseValue = NotOperator(matchesCaseValue);
|
||||
matchesDefault = BinaryOperator(llvm::Instruction::And, matchesDefault,
|
||||
notMatchesCaseValue, "default&~case_match");
|
||||
}
|
||||
|
||||
// The mask may have some lanes on, which corresponds to the previous
|
||||
// label falling through; compute the updated mask by ANDing with the
|
||||
// current mask.
|
||||
llvm::Value *oldMask = GetInternalMask();
|
||||
llvm::Value *newMask = BinaryOperator(llvm::Instruction::Or, oldMask,
|
||||
matchesDefault, "old_mask|matches_default");
|
||||
SetInternalMask(newMask);
|
||||
|
||||
if (checkMask)
|
||||
addSwitchMaskCheck(newMask);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FunctionEmitContext::EmitCaseLabel(int value, bool checkMask, SourcePos pos) {
|
||||
if (!inUniformSwitch() && !inVaryingSwitch()) {
|
||||
Error(pos, "\"case\" label illegal outside of \"switch\" statement.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the basic block for this case statement.
|
||||
llvm::BasicBlock *bbCase = NULL;
|
||||
Assert(caseBlocks != NULL);
|
||||
for (int i = 0; i < (int)caseBlocks->size(); ++i)
|
||||
if ((*caseBlocks)[i].first == value) {
|
||||
bbCase = (*caseBlocks)[i].second;
|
||||
break;
|
||||
}
|
||||
Assert(bbCase != NULL);
|
||||
|
||||
if (bblock != NULL)
|
||||
// fall through from the previous case
|
||||
BranchInst(bbCase);
|
||||
SetCurrentBasicBlock(bbCase);
|
||||
|
||||
if (inUniformSwitch())
|
||||
return;
|
||||
|
||||
// update the mask: first, get a mask that indicates which program
|
||||
// instances have a value for the switch expression that matches this
|
||||
// case statement.
|
||||
llvm::Value *valueVec = (switchExpr->getType() == LLVMTypes::Int32VectorType) ?
|
||||
LLVMInt32Vector(value) : LLVMInt64Vector(value);
|
||||
llvm::Value *matchesCaseValue =
|
||||
CmpInst(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_EQ, switchExpr,
|
||||
valueVec, "cmp_case_value");
|
||||
matchesCaseValue = I1VecToBoolVec(matchesCaseValue);
|
||||
|
||||
// If a lane was off going into the switch, we don't care if has a
|
||||
// value in the switch expression that happens to match this case.
|
||||
llvm::Value *entryMask = getMaskAtSwitchEntry();
|
||||
matchesCaseValue = BinaryOperator(llvm::Instruction::And, entryMask,
|
||||
matchesCaseValue, "entry_mask&case_match");
|
||||
|
||||
// Take the surviving lanes and turn on the mask for them.
|
||||
llvm::Value *oldMask = GetInternalMask();
|
||||
llvm::Value *newMask = BinaryOperator(llvm::Instruction::Or, oldMask,
|
||||
matchesCaseValue, "mask|case_match");
|
||||
SetInternalMask(newMask);
|
||||
|
||||
if (checkMask)
|
||||
addSwitchMaskCheck(newMask);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FunctionEmitContext::SwitchInst(llvm::Value *expr, llvm::BasicBlock *bbDefault,
|
||||
const std::vector<std::pair<int, llvm::BasicBlock *> > &bbCases,
|
||||
const std::map<llvm::BasicBlock *, llvm::BasicBlock *> &bbNext) {
|
||||
// The calling code should have called StartSwitch() before calling
|
||||
// SwitchInst().
|
||||
Assert(controlFlowInfo.size() &&
|
||||
controlFlowInfo.back()->IsSwitch());
|
||||
|
||||
switchExpr = expr;
|
||||
defaultBlock = bbDefault;
|
||||
caseBlocks = new std::vector<std::pair<int, llvm::BasicBlock *> >(bbCases);
|
||||
nextBlocks = new std::map<llvm::BasicBlock *, llvm::BasicBlock *>(bbNext);
|
||||
|
||||
if (inUniformSwitch()) {
|
||||
// For a uniform switch, just wire things up to the LLVM switch
|
||||
// instruction.
|
||||
Assert(llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(expr->getType()) ==
|
||||
false);
|
||||
llvm::SwitchInst *s = llvm::SwitchInst::Create(expr, bbDefault,
|
||||
bbCases.size(), bblock);
|
||||
for (int i = 0; i < (int)bbCases.size(); ++i) {
|
||||
if (expr->getType() == LLVMTypes::Int32Type)
|
||||
s->addCase(LLVMInt32(bbCases[i].first), bbCases[i].second);
|
||||
else {
|
||||
Assert(expr->getType() == LLVMTypes::Int64Type);
|
||||
s->addCase(LLVMInt64(bbCases[i].first), bbCases[i].second);
|
||||
}
|
||||
}
|
||||
|
||||
AddDebugPos(s);
|
||||
// switch is a terminator
|
||||
bblock = NULL;
|
||||
}
|
||||
else {
|
||||
// For a varying switch, we first turn off all lanes of the mask
|
||||
SetInternalMask(LLVMMaskAllOff);
|
||||
|
||||
if (nextBlocks->size() > 0) {
|
||||
// If there are any labels inside the switch, jump to the first
|
||||
// one; any code before the first label won't be executed by
|
||||
// anyone.
|
||||
std::map<llvm::BasicBlock *, llvm::BasicBlock *>::const_iterator iter;
|
||||
iter = nextBlocks->find(NULL);
|
||||
Assert(iter != nextBlocks->end());
|
||||
llvm::BasicBlock *bbFirst = iter->second;
|
||||
BranchInst(bbFirst);
|
||||
bblock = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
FunctionEmitContext::VaryingCFDepth() const {
|
||||
int sum = 0;
|
||||
@@ -905,6 +1243,14 @@ FunctionEmitContext::All(llvm::Value *mask) {
|
||||
}
|
||||
|
||||
|
||||
llvm::Value *
|
||||
FunctionEmitContext::None(llvm::Value *mask) {
|
||||
llvm::Value *mmval = LaneMask(mask);
|
||||
return CmpInst(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_EQ, mmval,
|
||||
LLVMInt32(0), "none_mm_cmp");
|
||||
}
|
||||
|
||||
|
||||
llvm::Value *
|
||||
FunctionEmitContext::LaneMask(llvm::Value *v) {
|
||||
// Call the target-dependent movmsk function to turn the vector mask
|
||||
@@ -2632,3 +2978,36 @@ FunctionEmitContext::addVaryingOffsetsIfNeeded(llvm::Value *ptr,
|
||||
|
||||
return BinaryOperator(llvm::Instruction::Add, ptr, offset);
|
||||
}
|
||||
|
||||
|
||||
CFInfo *
|
||||
FunctionEmitContext::popCFState() {
|
||||
Assert(controlFlowInfo.size() > 0);
|
||||
CFInfo *ci = controlFlowInfo.back();
|
||||
controlFlowInfo.pop_back();
|
||||
|
||||
if (ci->IsSwitch()) {
|
||||
breakTarget = ci->savedBreakTarget;
|
||||
continueTarget = ci->savedContinueTarget;
|
||||
breakLanesPtr = ci->savedBreakLanesPtr;
|
||||
continueLanesPtr = ci->savedContinueLanesPtr;
|
||||
loopMask = ci->savedLoopMask;
|
||||
switchExpr = ci->savedSwitchExpr;
|
||||
defaultBlock = ci->savedDefaultBlock;
|
||||
caseBlocks = ci->savedCaseBlocks;
|
||||
nextBlocks = ci->savedNextBlocks;
|
||||
}
|
||||
else if (ci->IsLoop() || ci->IsForeach()) {
|
||||
breakTarget = ci->savedBreakTarget;
|
||||
continueTarget = ci->savedContinueTarget;
|
||||
breakLanesPtr = ci->savedBreakLanesPtr;
|
||||
continueLanesPtr = ci->savedContinueLanesPtr;
|
||||
loopMask = ci->savedLoopMask;
|
||||
}
|
||||
else {
|
||||
Assert(ci->IsIf());
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
return ci;
|
||||
}
|
||||
|
||||
93
ctx.h
93
ctx.h
@@ -187,6 +187,45 @@ public:
|
||||
previous iteration. */
|
||||
void RestoreContinuedLanes();
|
||||
|
||||
/** Indicates that code generation for a "switch" statement is about to
|
||||
start. isUniform indicates whether the "switch" value is uniform,
|
||||
and bbAfterSwitch gives the basic block immediately following the
|
||||
"switch" statement. (For example, if the switch condition is
|
||||
uniform, we jump here upon executing a "break" statement.) */
|
||||
void StartSwitch(bool isUniform, llvm::BasicBlock *bbAfterSwitch);
|
||||
/** Indicates the end of code generation for a "switch" statement. */
|
||||
void EndSwitch();
|
||||
|
||||
/** Emits code for a "switch" statement in the program.
|
||||
@param expr Gives the value of the expression after the "switch"
|
||||
@param defaultBlock Basic block to execute for the "default" case. This
|
||||
should be NULL if there is no "default" label inside
|
||||
the switch.
|
||||
@param caseBlocks vector that stores the mapping from label values
|
||||
after "case" statements to basic blocks corresponding
|
||||
to the "case" labels.
|
||||
@param nextBlocks For each basic block for a "case" or "default"
|
||||
label, this gives the basic block for the
|
||||
immediately-following "case" or "default" label (or
|
||||
the basic block after the "switch" statement for the
|
||||
last label.)
|
||||
*/
|
||||
void SwitchInst(llvm::Value *expr, llvm::BasicBlock *defaultBlock,
|
||||
const std::vector<std::pair<int, llvm::BasicBlock *> > &caseBlocks,
|
||||
const std::map<llvm::BasicBlock *, llvm::BasicBlock *> &nextBlocks);
|
||||
|
||||
/** Generates code for a "default" label after a "switch" statement.
|
||||
The checkMask parameter indicates whether additional code should be
|
||||
generated to check to see if the execution mask is all off after
|
||||
the default label (in which case a jump to the following label will
|
||||
be issued. */
|
||||
void EmitDefaultLabel(bool checkMask, SourcePos pos);
|
||||
|
||||
/** Generates code for a "case" label after a "switch" statement. See
|
||||
the documentation for EmitDefaultLabel() for discussion of the
|
||||
checkMask parameter. */
|
||||
void EmitCaseLabel(int value, bool checkMask, SourcePos pos);
|
||||
|
||||
/** Returns the current number of nested levels of 'varying' control
|
||||
flow */
|
||||
int VaryingCFDepth() const;
|
||||
@@ -221,6 +260,10 @@ public:
|
||||
i1 value that indicates if all of the mask lanes are on. */
|
||||
llvm::Value *All(llvm::Value *mask);
|
||||
|
||||
/** Given a boolean mask value of type LLVMTypes::MaskType, return an
|
||||
i1 value that indicates if all of the mask lanes are off. */
|
||||
llvm::Value *None(llvm::Value *mask);
|
||||
|
||||
/** Given a boolean mask value of type LLVMTypes::MaskType, return an
|
||||
i32 value wherein the i'th bit is on if and only if the i'th lane
|
||||
of the mask is on. */
|
||||
@@ -492,10 +535,10 @@ private:
|
||||
the loop. */
|
||||
llvm::Value *loopMask;
|
||||
|
||||
/** If currently in a loop body, this is a pointer to memory to store a
|
||||
mask value that represents which of the lanes have executed a
|
||||
'break' statement. If we're not in a loop body, this should be
|
||||
NULL. */
|
||||
/** If currently in a loop body or switch statement, this is a pointer
|
||||
to memory to store a mask value that represents which of the lanes
|
||||
have executed a 'break' statement. If we're not in a loop body or
|
||||
switch, this should be NULL. */
|
||||
llvm::Value *breakLanesPtr;
|
||||
|
||||
/** Similar to breakLanesPtr, if we're inside a loop, this is a pointer
|
||||
@@ -503,16 +546,42 @@ private:
|
||||
'continue' statement. */
|
||||
llvm::Value *continueLanesPtr;
|
||||
|
||||
/** If we're inside a loop, this gives the basic block immediately
|
||||
after the current loop, which we will jump to if all of the lanes
|
||||
have executed a break statement or are otherwise done with the
|
||||
loop. */
|
||||
/** If we're inside a loop or switch statement, this gives the basic
|
||||
block immediately after the current loop or switch, which we will
|
||||
jump to if all of the lanes have executed a break statement or are
|
||||
otherwise done with it. */
|
||||
llvm::BasicBlock *breakTarget;
|
||||
|
||||
/** If we're inside a loop, this gives the block to jump to if all of
|
||||
the running lanes have executed a 'continue' statement. */
|
||||
llvm::BasicBlock *continueTarget;
|
||||
|
||||
/** @name Switch statement state
|
||||
|
||||
These variables store various state that's active when we're
|
||||
generating code for a switch statement. They should all be NULL
|
||||
outside of a switch.
|
||||
@{
|
||||
*/
|
||||
|
||||
/** The value of the expression used to determine which case in the
|
||||
statements after the switch to execute. */
|
||||
llvm::Value *switchExpr;
|
||||
|
||||
/** Map from case label numbers to the basic block that will hold code
|
||||
for that case. */
|
||||
const std::vector<std::pair<int, llvm::BasicBlock *> > *caseBlocks;
|
||||
|
||||
/** The basic block of code to run for the "default" label in the
|
||||
switch statement. */
|
||||
llvm::BasicBlock *defaultBlock;
|
||||
|
||||
/** For each basic block for the code for cases (and the default label,
|
||||
if present), this map gives the basic block for the immediately
|
||||
following case/default label. */
|
||||
const std::map<llvm::BasicBlock *, llvm::BasicBlock *> *nextBlocks;
|
||||
/** @} */
|
||||
|
||||
/** A pointer to memory that records which of the program instances
|
||||
have executed a 'return' statement (and are thus really truly done
|
||||
running any more instructions in this functions. */
|
||||
@@ -556,7 +625,7 @@ private:
|
||||
|
||||
llvm::Value *pointerVectorToVoidPointers(llvm::Value *value);
|
||||
static void addGSMetadata(llvm::Value *inst, SourcePos pos);
|
||||
bool ifsInLoopAllUniform() const;
|
||||
bool ifsInCFAllUniform(int cfType) const;
|
||||
void jumpIfAllLoopLanesAreDone(llvm::BasicBlock *target);
|
||||
llvm::Value *emitGatherCallback(llvm::Value *lvalue, llvm::Value *retPtr);
|
||||
|
||||
@@ -564,6 +633,12 @@ private:
|
||||
const Type *ptrType);
|
||||
|
||||
void restoreMaskGivenReturns(llvm::Value *oldMask);
|
||||
void addSwitchMaskCheck(llvm::Value *mask);
|
||||
bool inUniformSwitch() const;
|
||||
bool inVaryingSwitch() const;
|
||||
llvm::Value *getMaskAtSwitchEntry();
|
||||
|
||||
CFInfo *popCFState();
|
||||
|
||||
void scatter(llvm::Value *value, llvm::Value *ptr, const Type *ptrType,
|
||||
llvm::Value *mask);
|
||||
|
||||
@@ -99,6 +99,7 @@ Contents:
|
||||
+ `Control Flow`_
|
||||
|
||||
* `Conditional Statements: "if"`_
|
||||
* `Conditional Statements: "switch"`_
|
||||
* `Basic Iteration Statements: "for", "while", and "do"`_
|
||||
* `Unstructured Control Flow: "goto"`_
|
||||
* `"Coherent" Control Flow Statements: "cif" and Friends`_
|
||||
@@ -1994,6 +1995,31 @@ executes if the condition is false.
|
||||
else
|
||||
x *= 2.;
|
||||
|
||||
Conditional Statements: "switch"
|
||||
--------------------------------
|
||||
|
||||
The ``switch`` conditional statement is also available, again with the same
|
||||
behavior as in C; the expression used in the ``switch`` must be of integer
|
||||
type (but it can be uniform or varying). As in C, if there is no ``break``
|
||||
statement at the end of the code for a given case, execution "falls
|
||||
through" to the following case. These features are demonstrated in the
|
||||
code below.
|
||||
|
||||
::
|
||||
|
||||
int x = ...;
|
||||
switch (x) {
|
||||
case 0:
|
||||
case 1:
|
||||
foo(x);
|
||||
/* fall through */
|
||||
case 5:
|
||||
x = 0;
|
||||
break;
|
||||
default:
|
||||
x *= x;
|
||||
}
|
||||
|
||||
Basic Iteration Statements: "for", "while", and "do"
|
||||
----------------------------------------------------
|
||||
|
||||
|
||||
2
ispc.h
2
ispc.h
@@ -437,6 +437,8 @@ enum {
|
||||
COST_VARYING_IF = 3,
|
||||
COST_UNIFORM_LOOP = 4,
|
||||
COST_VARYING_LOOP = 6,
|
||||
COST_UNIFORM_SWITCH = 4,
|
||||
COST_VARYING_SWITCH = 12,
|
||||
COST_ASSERT = 8,
|
||||
|
||||
CHECK_MASK_AT_FUNCTION_START_COST = 16,
|
||||
|
||||
14
parse.yy
14
parse.yy
@@ -1265,9 +1265,17 @@ labeled_statement
|
||||
$$ = new LabeledStmt($1, $3, @1);
|
||||
}
|
||||
| TOKEN_CASE constant_expression ':' statement
|
||||
{ UNIMPLEMENTED; }
|
||||
{
|
||||
int value;
|
||||
if ($2 != NULL &&
|
||||
lGetConstantInt($2, &value, @2, "Case statement value")) {
|
||||
$$ = new CaseStmt(value, $4, Union(@1, @2));
|
||||
}
|
||||
else
|
||||
$$ = NULL;
|
||||
}
|
||||
| TOKEN_DEFAULT ':' statement
|
||||
{ UNIMPLEMENTED; }
|
||||
{ $$ = new DefaultStmt($3, @1); }
|
||||
;
|
||||
|
||||
start_scope
|
||||
@@ -1313,7 +1321,7 @@ selection_statement
|
||||
| TOKEN_CIF '(' expression ')' statement TOKEN_ELSE statement
|
||||
{ $$ = new IfStmt($3, $5, $7, true, @1); }
|
||||
| TOKEN_SWITCH '(' expression ')' statement
|
||||
{ UNIMPLEMENTED; }
|
||||
{ $$ = new SwitchStmt($3, $5, @1); }
|
||||
;
|
||||
|
||||
for_test
|
||||
|
||||
301
stmt.cpp
301
stmt.cpp
@@ -1886,6 +1886,307 @@ ForeachStmt::Print(int indent) const {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// CaseStmt
|
||||
|
||||
/** Given the statements following a 'case' or 'default' label, this
|
||||
function determines whether the mask should be checked to see if it is
|
||||
"all off" immediately after the label, before executing the code for
|
||||
the statements.
|
||||
*/
|
||||
static bool
|
||||
lCheckMask(Stmt *stmts) {
|
||||
if (stmts == NULL)
|
||||
return false;
|
||||
|
||||
int cost = EstimateCost(stmts);
|
||||
|
||||
bool safeToRunWithAllLanesOff = true;
|
||||
WalkAST(stmts, lCheckAllOffSafety, NULL, &safeToRunWithAllLanesOff);
|
||||
|
||||
// The mask should be checked if the code following the
|
||||
// 'case'/'default' is relatively complex, or if it would be unsafe to
|
||||
// run that code with the execution mask all off.
|
||||
return (cost > PREDICATE_SAFE_IF_STATEMENT_COST ||
|
||||
safeToRunWithAllLanesOff == false);
|
||||
}
|
||||
|
||||
|
||||
CaseStmt::CaseStmt(int v, Stmt *s, SourcePos pos)
|
||||
: Stmt(pos), value(v) {
|
||||
stmts = s;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CaseStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
ctx->EmitCaseLabel(value, lCheckMask(stmts), pos);
|
||||
if (stmts)
|
||||
stmts->EmitCode(ctx);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CaseStmt::Print(int indent) const {
|
||||
printf("%*cCase [%d] label", indent, ' ', value);
|
||||
pos.Print();
|
||||
printf("\n");
|
||||
stmts->Print(indent+4);
|
||||
}
|
||||
|
||||
|
||||
Stmt *
|
||||
CaseStmt::TypeCheck() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
CaseStmt::EstimateCost() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// DefaultStmt
|
||||
|
||||
DefaultStmt::DefaultStmt(Stmt *s, SourcePos pos)
|
||||
: Stmt(pos) {
|
||||
stmts = s;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DefaultStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
ctx->EmitDefaultLabel(lCheckMask(stmts), pos);
|
||||
if (stmts)
|
||||
stmts->EmitCode(ctx);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DefaultStmt::Print(int indent) const {
|
||||
printf("%*cDefault Stmt", indent, ' ');
|
||||
pos.Print();
|
||||
printf("\n");
|
||||
stmts->Print(indent+4);
|
||||
}
|
||||
|
||||
|
||||
Stmt *
|
||||
DefaultStmt::TypeCheck() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
DefaultStmt::EstimateCost() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// SwitchStmt
|
||||
|
||||
SwitchStmt::SwitchStmt(Expr *e, Stmt *s, SourcePos pos)
|
||||
: Stmt(pos) {
|
||||
expr = e;
|
||||
stmts = s;
|
||||
}
|
||||
|
||||
|
||||
/* An instance of this structure is carried along as we traverse the AST
|
||||
nodes for the statements after a "switch" statement. We use this
|
||||
structure to record all of the 'case' and 'default' statements after the
|
||||
"switch". */
|
||||
struct SwitchVisitInfo {
|
||||
SwitchVisitInfo(FunctionEmitContext *c) {
|
||||
ctx = c;
|
||||
defaultBlock = NULL;
|
||||
lastBlock = NULL;
|
||||
}
|
||||
|
||||
FunctionEmitContext *ctx;
|
||||
|
||||
/* Basic block for the code following the "default" label (if any). */
|
||||
llvm::BasicBlock *defaultBlock;
|
||||
|
||||
/* Map from integer values after "case" labels to the basic blocks that
|
||||
follow the corresponding "case" label. */
|
||||
std::vector<std::pair<int, llvm::BasicBlock *> > caseBlocks;
|
||||
|
||||
/* For each basic block for a "case" label or a "default" label,
|
||||
nextBlock[block] stores the basic block pointer for the next
|
||||
subsequent "case" or "default" label in the program. */
|
||||
std::map<llvm::BasicBlock *, llvm::BasicBlock *> nextBlock;
|
||||
|
||||
/* The last basic block created for a "case" or "default" label; when
|
||||
we create the basic block for the next one, we'll use this to update
|
||||
the nextBlock map<> above. */
|
||||
llvm::BasicBlock *lastBlock;
|
||||
};
|
||||
|
||||
|
||||
static bool
|
||||
lSwitchASTPreVisit(ASTNode *node, void *d) {
|
||||
if (dynamic_cast<SwitchStmt *>(node) != NULL)
|
||||
// don't continue recursively into a nested switch--we only want
|
||||
// our own case and default statements!
|
||||
return false;
|
||||
|
||||
CaseStmt *cs = dynamic_cast<CaseStmt *>(node);
|
||||
DefaultStmt *ds = dynamic_cast<DefaultStmt *>(node);
|
||||
|
||||
SwitchVisitInfo *svi = (SwitchVisitInfo *)d;
|
||||
llvm::BasicBlock *bb = NULL;
|
||||
if (cs != NULL) {
|
||||
// Complain if we've seen a case statement with the same value
|
||||
// already
|
||||
for (int i = 0; i < (int)svi->caseBlocks.size(); ++i) {
|
||||
if (svi->caseBlocks[i].first == cs->value) {
|
||||
Error(cs->pos, "Duplicate case value \"%d\".", cs->value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise create a new basic block for the code following this
|
||||
// 'case' statement and record the mappign between the case label
|
||||
// value and the basic block
|
||||
char buf[32];
|
||||
sprintf(buf, "case_%d", cs->value);
|
||||
bb = svi->ctx->CreateBasicBlock(buf);
|
||||
svi->caseBlocks.push_back(std::make_pair(cs->value, bb));
|
||||
}
|
||||
else if (ds != NULL) {
|
||||
// And complain if we've seen another 'default' label..
|
||||
if (svi->defaultBlock != NULL) {
|
||||
Error(ds->pos, "Multiple \"default\" lables in switch statement.");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Otherwise create a basic block for the code following the
|
||||
// "default".
|
||||
bb = svi->ctx->CreateBasicBlock("default");
|
||||
svi->defaultBlock = bb;
|
||||
}
|
||||
}
|
||||
|
||||
// If we saw a "case" or "default" label, then update the map to record
|
||||
// that the block we just created follows the block created for the
|
||||
// previous label in the "switch".
|
||||
if (bb != NULL) {
|
||||
svi->nextBlock[svi->lastBlock] = bb;
|
||||
svi->lastBlock = bb;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SwitchStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
if (ctx->GetCurrentBasicBlock() == NULL)
|
||||
return;
|
||||
|
||||
const Type *type;
|
||||
if (expr == NULL || ((type = expr->GetType()) == NULL)) {
|
||||
Assert(m->errorCount > 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Basic block we'll end up after the switch statement
|
||||
llvm::BasicBlock *bbDone = ctx->CreateBasicBlock("switch_done");
|
||||
|
||||
// Walk the AST of the statements after the 'switch' to collect a bunch
|
||||
// of information about the structure of the 'case' and 'default'
|
||||
// statements.
|
||||
SwitchVisitInfo svi(ctx);
|
||||
WalkAST(stmts, lSwitchASTPreVisit, NULL, &svi);
|
||||
// Record that the basic block following the last one created for a
|
||||
// case/default is the block after the end of the switch statement.
|
||||
svi.nextBlock[svi.lastBlock] = bbDone;
|
||||
|
||||
llvm::Value *exprValue = expr->GetValue(ctx);
|
||||
if (exprValue == NULL) {
|
||||
Assert(m->errorCount > 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->StartSwitch(type->IsUniformType(), bbDone);
|
||||
ctx->SwitchInst(exprValue, svi.defaultBlock ? svi.defaultBlock : bbDone,
|
||||
svi.caseBlocks, svi.nextBlock);
|
||||
|
||||
if (stmts != NULL)
|
||||
stmts->EmitCode(ctx);
|
||||
|
||||
if (ctx->GetCurrentBasicBlock() != NULL)
|
||||
ctx->BranchInst(bbDone);
|
||||
|
||||
ctx->SetCurrentBasicBlock(bbDone);
|
||||
ctx->EndSwitch();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SwitchStmt::Print(int indent) const {
|
||||
printf("%*cSwitch Stmt", indent, ' ');
|
||||
pos.Print();
|
||||
printf("\n");
|
||||
printf("%*cexpr = ", indent, ' ');
|
||||
expr->Print();
|
||||
printf("\n");
|
||||
stmts->Print(indent+4);
|
||||
}
|
||||
|
||||
|
||||
Stmt *
|
||||
SwitchStmt::TypeCheck() {
|
||||
const Type *exprType = expr->GetType();
|
||||
if (exprType == NULL)
|
||||
return NULL;
|
||||
|
||||
const Type *toType = NULL;
|
||||
exprType = exprType->GetAsConstType();
|
||||
bool is64bit = (exprType->GetAsUniformType() ==
|
||||
AtomicType::UniformConstUInt64 ||
|
||||
exprType->GetAsUniformType() ==
|
||||
AtomicType::UniformConstInt64);
|
||||
|
||||
// FIXME: if there's a break or continue under varying control flow
|
||||
// within a switch with a "uniform" condition, we promote the condition
|
||||
// to varying so that everything works out and we are set to handle the
|
||||
// resulting divergent control flow. This is somewhat sub-optimal; see
|
||||
// Issue #XXX for details.
|
||||
bool isUniform = (exprType->IsUniformType() &&
|
||||
lHasVaryingBreakOrContinue(stmts) == false);
|
||||
|
||||
if (isUniform) {
|
||||
if (is64bit) toType = AtomicType::UniformInt64;
|
||||
else toType = AtomicType::UniformInt32;
|
||||
}
|
||||
else {
|
||||
if (is64bit) toType = AtomicType::VaryingInt64;
|
||||
else toType = AtomicType::VaryingInt32;
|
||||
}
|
||||
|
||||
expr = TypeConvertExpr(expr, toType, "switch expression");
|
||||
if (expr == NULL)
|
||||
return NULL;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SwitchStmt::EstimateCost() const {
|
||||
const Type *type = expr->GetType();
|
||||
if (type && type->IsVaryingType())
|
||||
return COST_VARYING_SWITCH;
|
||||
else
|
||||
return COST_UNIFORM_SWITCH;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ReturnStmt
|
||||
|
||||
|
||||
59
stmt.h
59
stmt.h
@@ -282,6 +282,60 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/** Statement corresponding to a "case" label in the program. In addition
|
||||
to the value associated with the "case", this statement also stores the
|
||||
statements following it. */
|
||||
class CaseStmt : public Stmt {
|
||||
public:
|
||||
CaseStmt(int value, Stmt *stmt, SourcePos pos);
|
||||
|
||||
void EmitCode(FunctionEmitContext *ctx) const;
|
||||
void Print(int indent) const;
|
||||
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
/** Integer value after the "case" statement */
|
||||
const int value;
|
||||
Stmt *stmts;
|
||||
};
|
||||
|
||||
|
||||
/** Statement for a "default" label (as would be found inside a "switch"
|
||||
statement). */
|
||||
class DefaultStmt : public Stmt {
|
||||
public:
|
||||
DefaultStmt(Stmt *stmt, SourcePos pos);
|
||||
|
||||
void EmitCode(FunctionEmitContext *ctx) const;
|
||||
void Print(int indent) const;
|
||||
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
Stmt *stmts;
|
||||
};
|
||||
|
||||
|
||||
/** A "switch" statement in the program. */
|
||||
class SwitchStmt : public Stmt {
|
||||
public:
|
||||
SwitchStmt(Expr *expr, Stmt *stmts, SourcePos pos);
|
||||
|
||||
void EmitCode(FunctionEmitContext *ctx) const;
|
||||
void Print(int indent) const;
|
||||
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
/** Expression that is used to determine which label to jump to. */
|
||||
Expr *expr;
|
||||
/** Statement block after the "switch" expression. */
|
||||
Stmt *stmts;
|
||||
};
|
||||
|
||||
|
||||
/** A "goto" in an ispc program. */
|
||||
class GotoStmt : public Stmt {
|
||||
public:
|
||||
GotoStmt(const char *label, SourcePos gotoPos, SourcePos idPos);
|
||||
@@ -293,11 +347,14 @@ public:
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
/** Name of the label to jump to when the goto is executed. */
|
||||
std::string label;
|
||||
SourcePos identifierPos;
|
||||
};
|
||||
|
||||
|
||||
/** Statement corresponding to a label (as would be used as a goto target)
|
||||
in the program. */
|
||||
class LabeledStmt : public Stmt {
|
||||
public:
|
||||
LabeledStmt(const char *label, Stmt *stmt, SourcePos p);
|
||||
@@ -309,7 +366,9 @@ public:
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
/** Name of the label. */
|
||||
std::string name;
|
||||
/** Statements following the label. */
|
||||
Stmt *stmt;
|
||||
};
|
||||
|
||||
|
||||
18
tests/switch-1.ispc
Normal file
18
tests/switch-1.ispc
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
switch (b) {
|
||||
default:
|
||||
RET[programIndex] = -1;
|
||||
break;
|
||||
case 5:
|
||||
RET[programIndex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
}
|
||||
44
tests/switch-10.ispc
Normal file
44
tests/switch-10.ispc
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int switchit(int a, uniform int b) {
|
||||
switch (a) {
|
||||
case 3:
|
||||
return 1;
|
||||
case 7:
|
||||
case 6:
|
||||
case 4:
|
||||
case 5:
|
||||
if (a & 1)
|
||||
break;
|
||||
return 2;
|
||||
case 1: {
|
||||
switch (a+b) {
|
||||
case 6:
|
||||
return 42;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1234;
|
||||
}
|
||||
case 32:
|
||||
*((int *)NULL) = 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
int x = switchit(a, b);
|
||||
RET[programIndex] = x;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
RET[0] = 42;
|
||||
RET[2] = 1;
|
||||
RET[6] = RET[4] = 3;
|
||||
RET[5] = RET[3] = 2;
|
||||
}
|
||||
50
tests/switch-11.ispc
Normal file
50
tests/switch-11.ispc
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int switchit(int a, uniform int b) {
|
||||
switch (a) {
|
||||
case 3:
|
||||
return 1;
|
||||
case 7:
|
||||
case 6:
|
||||
case 4:
|
||||
case 5:
|
||||
if (a & 1)
|
||||
break;
|
||||
return 2;
|
||||
case 1: {
|
||||
switch (a+b) {
|
||||
case 60:
|
||||
return -1234;
|
||||
default:
|
||||
break;
|
||||
case 6:
|
||||
if (b == 5)
|
||||
break;
|
||||
return -42;
|
||||
case 12:
|
||||
return -1;
|
||||
}
|
||||
return 42;
|
||||
}
|
||||
case 32:
|
||||
*((int *)NULL) = 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
int x = switchit(a, b);
|
||||
RET[programIndex] = x;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
RET[0] = 42;
|
||||
RET[2] = 1;
|
||||
RET[6] = RET[4] = 3;
|
||||
RET[5] = RET[3] = 2;
|
||||
}
|
||||
54
tests/switch-12.ispc
Normal file
54
tests/switch-12.ispc
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int switchit(int a, uniform int b) {
|
||||
switch (a) {
|
||||
case 3:
|
||||
return 1;
|
||||
case 7:
|
||||
case 6:
|
||||
case 4:
|
||||
case 5:
|
||||
if (a & 1)
|
||||
break;
|
||||
return 2;
|
||||
case 1: {
|
||||
switch (a+b) {
|
||||
case 60:
|
||||
return -1234;
|
||||
default:
|
||||
break;
|
||||
case 6:
|
||||
int count = 0;
|
||||
for (count = 0; count < 10; ++count) {
|
||||
a += b;
|
||||
if (a == 11)
|
||||
break;
|
||||
}
|
||||
return a;
|
||||
case 12:
|
||||
return -1;
|
||||
}
|
||||
return 42;
|
||||
}
|
||||
case 32:
|
||||
*((int *)NULL) = 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
int x = switchit(a, b);
|
||||
RET[programIndex] = x;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
RET[0] = 11;
|
||||
RET[2] = 1;
|
||||
RET[6] = RET[4] = 3;
|
||||
RET[5] = RET[3] = 2;
|
||||
}
|
||||
17
tests/switch-2.ispc
Normal file
17
tests/switch-2.ispc
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
switch (b) {
|
||||
default:
|
||||
RET[programIndex] = -1;
|
||||
case 5:
|
||||
RET[programIndex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
}
|
||||
18
tests/switch-3.ispc
Normal file
18
tests/switch-3.ispc
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
switch (b) {
|
||||
case 5:
|
||||
RET[programIndex] = 0;
|
||||
break;
|
||||
default:
|
||||
RET[programIndex] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
}
|
||||
24
tests/switch-4.ispc
Normal file
24
tests/switch-4.ispc
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int switchit(int a, uniform int b) {
|
||||
int r = 0;
|
||||
switch (a) {
|
||||
case 3:
|
||||
r = 1;
|
||||
break;
|
||||
default:
|
||||
r = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
int x = switchit(a, b);
|
||||
RET[programIndex] = x;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = (programIndex == 2) ? 1 : 0;
|
||||
}
|
||||
22
tests/switch-5.ispc
Normal file
22
tests/switch-5.ispc
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int switchit(int a, uniform int b) {
|
||||
int r = 0;
|
||||
switch (a) {
|
||||
case 3:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
int x = switchit(a, b);
|
||||
RET[programIndex] = x;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = (programIndex == 2) ? 1 : 0;
|
||||
}
|
||||
27
tests/switch-6.ispc
Normal file
27
tests/switch-6.ispc
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int switchit(int a, uniform int b) {
|
||||
switch (a) {
|
||||
case 3:
|
||||
return 1;
|
||||
case 7:
|
||||
if (b == 5)
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
int x = switchit(a, b);
|
||||
RET[programIndex] = x;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
RET[2] = 1;
|
||||
RET[6] = -1;
|
||||
}
|
||||
32
tests/switch-7.ispc
Normal file
32
tests/switch-7.ispc
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int switchit(int a, uniform int b) {
|
||||
switch (a) {
|
||||
case 3:
|
||||
return 1;
|
||||
case 7:
|
||||
case 6:
|
||||
case 4:
|
||||
case 5:
|
||||
if (a & 1)
|
||||
break;
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
int x = switchit(a, b);
|
||||
RET[programIndex] = x;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
RET[2] = 1;
|
||||
RET[6] = RET[4] = 3;
|
||||
RET[5] = RET[3] = 2;
|
||||
}
|
||||
36
tests/switch-8.ispc
Normal file
36
tests/switch-8.ispc
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int switchit(int a, uniform int b) {
|
||||
switch (a) {
|
||||
case 3:
|
||||
return 1;
|
||||
case 7:
|
||||
case 6:
|
||||
case 4:
|
||||
case 5:
|
||||
if (a & 1)
|
||||
break;
|
||||
return 2;
|
||||
case 32:
|
||||
*((int *)NULL) = 0;
|
||||
//CO default:
|
||||
case 1:
|
||||
case 2:
|
||||
return 0;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
int x = switchit(a, b);
|
||||
RET[programIndex] = x;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
RET[2] = 1;
|
||||
RET[6] = RET[4] = 3;
|
||||
RET[5] = RET[3] = 2;
|
||||
}
|
||||
34
tests/switch-9.ispc
Normal file
34
tests/switch-9.ispc
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int switchit(int a, uniform int b) {
|
||||
switch (a) {
|
||||
case 3:
|
||||
return 1;
|
||||
case 7:
|
||||
case 6:
|
||||
case 4:
|
||||
case 5:
|
||||
if (a & 1)
|
||||
break;
|
||||
return 2;
|
||||
case 32:
|
||||
*((int *)NULL) = 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
int a = aFOO[programIndex];
|
||||
int x = switchit(a, b);
|
||||
RET[programIndex] = x;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 0;
|
||||
RET[2] = 1;
|
||||
RET[6] = RET[4] = 3;
|
||||
RET[5] = RET[3] = 2;
|
||||
}
|
||||
9
tests_errors/switch-1.ispc
Normal file
9
tests_errors/switch-1.ispc
Normal file
@@ -0,0 +1,9 @@
|
||||
// Case statement value must be a compile-time integer constant
|
||||
|
||||
void foo(float f) {
|
||||
switch (f) {
|
||||
case 1.5:
|
||||
++f;
|
||||
}
|
||||
}
|
||||
|
||||
12
tests_errors/switch-2.ispc
Normal file
12
tests_errors/switch-2.ispc
Normal file
@@ -0,0 +1,12 @@
|
||||
// Duplicate case value "1"
|
||||
|
||||
void foo(float f) {
|
||||
switch (f) {
|
||||
case 1:
|
||||
++f;
|
||||
case 2:
|
||||
case 1:
|
||||
f = 0;
|
||||
}
|
||||
}
|
||||
|
||||
13
tests_errors/switch-3.ispc
Normal file
13
tests_errors/switch-3.ispc
Normal file
@@ -0,0 +1,13 @@
|
||||
// "case" label illegal outside of "switch" statement
|
||||
|
||||
void foo(float f) {
|
||||
switch (f) {
|
||||
case 1:
|
||||
++f;
|
||||
case 2:
|
||||
f = 0;
|
||||
}
|
||||
case 3:
|
||||
--f;
|
||||
}
|
||||
|
||||
13
tests_errors/switch-4.ispc
Normal file
13
tests_errors/switch-4.ispc
Normal file
@@ -0,0 +1,13 @@
|
||||
// "default" label illegal outside of "switch" statement
|
||||
|
||||
void foo(float f) {
|
||||
default:
|
||||
++f;
|
||||
switch (f) {
|
||||
case 1:
|
||||
++f;
|
||||
case 2:
|
||||
f = 0;
|
||||
}
|
||||
}
|
||||
|
||||
14
tests_errors/switch-5.ispc
Normal file
14
tests_errors/switch-5.ispc
Normal file
@@ -0,0 +1,14 @@
|
||||
// "default" label illegal outside of "switch" statement
|
||||
|
||||
void foo(float f) {
|
||||
default:
|
||||
++f;
|
||||
switch (f) {
|
||||
case 1:
|
||||
++f;
|
||||
continue;
|
||||
case 2:
|
||||
f = 0;
|
||||
}
|
||||
}
|
||||
|
||||
12
tests_errors/switch-6.ispc
Normal file
12
tests_errors/switch-6.ispc
Normal file
@@ -0,0 +1,12 @@
|
||||
// "continue" statement illegal outside of for/while/do/foreach loops
|
||||
|
||||
void foo(float f) {
|
||||
switch (f) {
|
||||
case 1:
|
||||
++f;
|
||||
continue;
|
||||
case 2:
|
||||
f = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user