Add foreach_unique iteration construct.
Idea via Ingo Wald / IVL compiler.
This commit is contained in:
12
ast.cpp
12
ast.cpp
@@ -92,6 +92,7 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
||||
DoStmt *dos;
|
||||
ForStmt *fs;
|
||||
ForeachStmt *fes;
|
||||
ForeachUniqueStmt *fus;
|
||||
CaseStmt *cs;
|
||||
DefaultStmt *defs;
|
||||
SwitchStmt *ss;
|
||||
@@ -137,6 +138,10 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
||||
postFunc, data);
|
||||
fes->stmts = (Stmt *)WalkAST(fes->stmts, preFunc, postFunc, data);
|
||||
}
|
||||
else if ((fus = dynamic_cast<ForeachUniqueStmt *>(node)) != NULL) {
|
||||
fus->expr = (Expr *)WalkAST(fus->expr, preFunc, postFunc, data);
|
||||
fus->stmts = (Stmt *)WalkAST(fus->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)
|
||||
@@ -385,12 +390,17 @@ lCheckAllOffSafety(ASTNode *node, void *data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dynamic_cast<ForeachStmt *>(node) != NULL) {
|
||||
if (dynamic_cast<ForeachStmt *>(node) != NULL ||
|
||||
dynamic_cast<ForeachUniqueStmt *>(node) != NULL) {
|
||||
// foreach() statements also shouldn't be run with an all-off mask.
|
||||
// Since they re-establish an 'all on' mask, this would be pretty
|
||||
// unintuitive. (More generally, it's possibly a little strange to
|
||||
// allow foreach() in the presence of any non-uniform control
|
||||
// flow...)
|
||||
//
|
||||
// Similarly, the implementation foreach_unique assumes as a
|
||||
// precondition that the mask won't be all off going into it, so
|
||||
// we'll enforce that here...
|
||||
*okPtr = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ Contents:
|
||||
* `Conditional Statements: "if"`_
|
||||
* `Conditional Statements: "switch"`_
|
||||
* `Basic Iteration Statements: "for", "while", and "do"`_
|
||||
* `Iteration over unique elements: "foreach_unique"`_
|
||||
* `Unstructured Control Flow: "goto"`_
|
||||
* `"Coherent" Control Flow Statements: "cif" and Friends`_
|
||||
* `Parallel Iteration Statements: "foreach" and "foreach_tiled"`_
|
||||
@@ -2372,6 +2373,44 @@ 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.
|
||||
|
||||
|
||||
Iteration over unique elements: "foreach_unique"
|
||||
------------------------------------------------
|
||||
|
||||
It can be useful to iterate over the elements of a varying variable,
|
||||
processing the subsets of of them that have the same value together. For
|
||||
example, consider a varying variable ``x`` that has the values ``{1, 2, 2,
|
||||
1, 1, 0, 0, 0}``, where the program is running on a target with a gang size
|
||||
of 8 program instances. Here, ``x`` has three unique values across the
|
||||
program instances: ``0``, ``1``, and ``2``.
|
||||
|
||||
The ``foreach_unique`` looping construct allows us to iterate over these
|
||||
unique values. In the code below, the ``foreach_unique`` loop body
|
||||
executes once for each of the three unique values, with execution mask set
|
||||
to match the program instances where the varying value matches the current
|
||||
unique value being processed.
|
||||
|
||||
::
|
||||
|
||||
int x = ...; // assume {1, 2, 2, 1, 1, 0, 0, 0}
|
||||
foreach_unique (val in x) {
|
||||
extern void func(uniform int v);
|
||||
func(val);
|
||||
}
|
||||
|
||||
In the above, ``func()`` will be called three times, once with value 0,
|
||||
once with value 1, and once with value 2. When it is called for value 0,
|
||||
only the last three program instances will be executing, and so forth. The
|
||||
order in which the loop executes for the unique values isn't defined.
|
||||
|
||||
The varying expression that provides the values to be iterated over is only
|
||||
evaluated once, and it must be of an atomic type (``float``, ``int``,
|
||||
etc.), an ``enum`` type, or a pointer type. The iteration variable ``val``
|
||||
is a variable of ``const uniform`` type of the iteration type; it can't be
|
||||
modified within the loop. Finally, ``break`` and ``return`` statements are
|
||||
illegal within the loop body, but ``continue`` statemetns are allowed.
|
||||
|
||||
|
||||
Unstructured Control Flow: "goto"
|
||||
---------------------------------
|
||||
|
||||
|
||||
9
lex.ll
9
lex.ll
@@ -66,7 +66,8 @@ static int allTokens[] = {
|
||||
TOKEN_CONST, TOKEN_CONTINUE, TOKEN_CRETURN, TOKEN_DEFAULT, TOKEN_DO,
|
||||
TOKEN_DELETE, TOKEN_DOUBLE, TOKEN_ELSE, TOKEN_ENUM,
|
||||
TOKEN_EXPORT, TOKEN_EXTERN, TOKEN_FALSE, TOKEN_FLOAT, TOKEN_FOR,
|
||||
TOKEN_FOREACH, TOKEN_FOREACH_TILED, TOKEN_GOTO, TOKEN_IF, TOKEN_INLINE,
|
||||
TOKEN_FOREACH, TOKEN_FOREACH_TILED, TOKEN_FOREACH_UNIQUE,
|
||||
TOKEN_GOTO, TOKEN_IF, TOKEN_IN, TOKEN_INLINE,
|
||||
TOKEN_INT, TOKEN_INT8, TOKEN_INT16, TOKEN_INT, TOKEN_INT64, TOKEN_LAUNCH,
|
||||
TOKEN_NEW, TOKEN_NULL, TOKEN_PRINT, TOKEN_RETURN, TOKEN_SOA, TOKEN_SIGNED,
|
||||
TOKEN_SIZEOF, TOKEN_STATIC, TOKEN_STRUCT, TOKEN_SWITCH, TOKEN_SYNC,
|
||||
@@ -115,8 +116,10 @@ void ParserInit() {
|
||||
tokenToName[TOKEN_FOR] = "for";
|
||||
tokenToName[TOKEN_FOREACH] = "foreach";
|
||||
tokenToName[TOKEN_FOREACH_TILED] = "foreach_tiled";
|
||||
tokenToName[TOKEN_FOREACH_UNIQUE] = "foreach_unique";
|
||||
tokenToName[TOKEN_GOTO] = "goto";
|
||||
tokenToName[TOKEN_IF] = "if";
|
||||
tokenToName[TOKEN_IN] = "in";
|
||||
tokenToName[TOKEN_INLINE] = "inline";
|
||||
tokenToName[TOKEN_INT] = "int";
|
||||
tokenToName[TOKEN_INT8] = "int8";
|
||||
@@ -223,9 +226,11 @@ void ParserInit() {
|
||||
tokenNameRemap["TOKEN_FOR"] = "\'for\'";
|
||||
tokenNameRemap["TOKEN_FOREACH"] = "\'foreach\'";
|
||||
tokenNameRemap["TOKEN_FOREACH_TILED"] = "\'foreach_tiled\'";
|
||||
tokenNameRemap["TOKEN_FOREACH_UNIQUE"] = "\'foreach_unique\'";
|
||||
tokenNameRemap["TOKEN_GOTO"] = "\'goto\'";
|
||||
tokenNameRemap["TOKEN_IDENTIFIER"] = "identifier";
|
||||
tokenNameRemap["TOKEN_IF"] = "\'if\'";
|
||||
tokenNameRemap["TOKEN_IN"] = "\'in\'";
|
||||
tokenNameRemap["TOKEN_INLINE"] = "\'inline\'";
|
||||
tokenNameRemap["TOKEN_INT"] = "\'int\'";
|
||||
tokenNameRemap["TOKEN_INT8"] = "\'int8\'";
|
||||
@@ -365,8 +370,10 @@ for { RT; return TOKEN_FOR; }
|
||||
__foreach_active { RT; return TOKEN_FOREACH_ACTIVE; }
|
||||
foreach { RT; return TOKEN_FOREACH; }
|
||||
foreach_tiled { RT; return TOKEN_FOREACH_TILED; }
|
||||
foreach_unique { RT; return TOKEN_FOREACH_UNIQUE; }
|
||||
goto { RT; return TOKEN_GOTO; }
|
||||
if { RT; return TOKEN_IF; }
|
||||
in { RT; return TOKEN_IN; }
|
||||
inline { RT; return TOKEN_INLINE; }
|
||||
int { RT; return TOKEN_INT; }
|
||||
int8 { RT; return TOKEN_INT8; }
|
||||
|
||||
37
parse.yy
37
parse.yy
@@ -117,7 +117,8 @@ static const char *lBuiltinTokens[] = {
|
||||
"assert", "bool", "break", "case", "cbreak", "ccontinue", "cdo",
|
||||
"cfor", "cif", "cwhile", "const", "continue", "creturn", "default",
|
||||
"do", "delete", "double", "else", "enum", "export", "extern", "false",
|
||||
"float", "for", "foreach", "foreach_tiled", "goto", "if", "inline",
|
||||
"float", "for", "foreach", "foreach_tiled", "foreach_unique",
|
||||
"goto", "if", "in", "inline",
|
||||
"int", "int8", "int16", "int32", "int64", "launch", "new", "NULL",
|
||||
"print", "return", "signed", "sizeof", "static", "struct", "switch",
|
||||
"sync", "task", "true", "typedef", "uniform", "unsigned", "varying",
|
||||
@@ -185,7 +186,7 @@ struct ForeachDimension {
|
||||
%token TOKEN_AND_OP TOKEN_OR_OP TOKEN_MUL_ASSIGN TOKEN_DIV_ASSIGN TOKEN_MOD_ASSIGN
|
||||
%token TOKEN_ADD_ASSIGN TOKEN_SUB_ASSIGN TOKEN_LEFT_ASSIGN TOKEN_RIGHT_ASSIGN
|
||||
%token TOKEN_AND_ASSIGN TOKEN_OR_ASSIGN TOKEN_XOR_ASSIGN
|
||||
%token TOKEN_SIZEOF TOKEN_NEW TOKEN_DELETE
|
||||
%token TOKEN_SIZEOF TOKEN_NEW TOKEN_DELETE TOKEN_IN
|
||||
|
||||
%token TOKEN_EXTERN TOKEN_EXPORT TOKEN_STATIC TOKEN_INLINE TOKEN_TASK TOKEN_DECLSPEC
|
||||
%token TOKEN_UNIFORM TOKEN_VARYING TOKEN_TYPEDEF TOKEN_SOA
|
||||
@@ -195,7 +196,7 @@ struct ForeachDimension {
|
||||
|
||||
%token TOKEN_CASE TOKEN_DEFAULT TOKEN_IF TOKEN_ELSE TOKEN_SWITCH
|
||||
%token TOKEN_WHILE TOKEN_DO TOKEN_LAUNCH TOKEN_FOREACH TOKEN_FOREACH_TILED
|
||||
%token TOKEN_FOREACH_ACTIVE TOKEN_DOTDOTDOT
|
||||
%token TOKEN_FOREACH_UNIQUE TOKEN_FOREACH_ACTIVE TOKEN_DOTDOTDOT
|
||||
%token TOKEN_FOR TOKEN_GOTO TOKEN_CONTINUE TOKEN_BREAK TOKEN_RETURN
|
||||
%token TOKEN_CIF TOKEN_CDO TOKEN_CFOR TOKEN_CWHILE TOKEN_CBREAK
|
||||
%token TOKEN_CCONTINUE TOKEN_CRETURN TOKEN_SYNC TOKEN_PRINT TOKEN_ASSERT
|
||||
@@ -241,7 +242,9 @@ struct ForeachDimension {
|
||||
%type <declSpecs> declaration_specifiers
|
||||
|
||||
%type <stringVal> string_constant
|
||||
%type <constCharPtr> struct_or_union_name enum_identifier goto_identifier
|
||||
%type <constCharPtr> struct_or_union_name enum_identifier goto_identifier
|
||||
%type <constCharPtr> foreach_unique_identifier
|
||||
|
||||
%type <intVal> int_constant soa_width_specifier rate_qualified_new
|
||||
|
||||
%type <foreachDimension> foreach_dimension_specifier
|
||||
@@ -1703,6 +1706,14 @@ foreach_dimension_list
|
||||
}
|
||||
;
|
||||
|
||||
foreach_unique_scope
|
||||
: TOKEN_FOREACH_UNIQUE { m->symbolTable->PushScope(); }
|
||||
;
|
||||
|
||||
foreach_unique_identifier
|
||||
: TOKEN_IDENTIFIER { $$ = yylval.stringVal->c_str(); }
|
||||
;
|
||||
|
||||
iteration_statement
|
||||
: TOKEN_WHILE '(' expression ')' statement
|
||||
{ $$ = new ForStmt(NULL, $3, NULL, $5, false, @1); }
|
||||
@@ -1795,6 +1806,24 @@ iteration_statement
|
||||
$$ = CreateForeachActiveStmt($3, $6, Union(@1, @4));
|
||||
m->symbolTable->PopScope();
|
||||
}
|
||||
| foreach_unique_scope '(' foreach_unique_identifier TOKEN_IN
|
||||
expression ')'
|
||||
{
|
||||
Expr *expr = $5;
|
||||
const Type *type;
|
||||
if (expr != NULL &&
|
||||
(expr = TypeCheck(expr)) != NULL &&
|
||||
(type = expr->GetType()) != NULL) {
|
||||
const Type *iterType = type->GetAsUniformType()->GetAsConstType();
|
||||
Symbol *sym = new Symbol($3, @3, iterType);
|
||||
m->symbolTable->AddVariable(sym);
|
||||
}
|
||||
}
|
||||
statement
|
||||
{
|
||||
$$ = new ForeachUniqueStmt($3, $5, $8, @1);
|
||||
m->symbolTable->PopScope();
|
||||
}
|
||||
;
|
||||
|
||||
goto_identifier
|
||||
|
||||
236
stmt.cpp
236
stmt.cpp
@@ -1915,6 +1915,242 @@ ForeachStmt::Print(int indent) const {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ForeachUniqueStmt
|
||||
|
||||
ForeachUniqueStmt::ForeachUniqueStmt(const char *iterName, Expr *e,
|
||||
Stmt *s, SourcePos pos)
|
||||
: Stmt(pos) {
|
||||
sym = m->symbolTable->LookupVariable(iterName);
|
||||
expr = e;
|
||||
stmts = s;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ForeachUniqueStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
if (!ctx->GetCurrentBasicBlock())
|
||||
return;
|
||||
|
||||
// First, allocate local storage for the symbol that we'll use for the
|
||||
// uniform variable that holds the current unique value through each
|
||||
// loop.
|
||||
if (sym->type == NULL) {
|
||||
Assert(m->errorCount > 0);
|
||||
return;
|
||||
}
|
||||
llvm::Type *symType = sym->type->LLVMType(g->ctx);
|
||||
if (symType == NULL) {
|
||||
Assert(m->errorCount > 0);
|
||||
return;
|
||||
}
|
||||
sym->storagePtr = ctx->AllocaInst(symType, sym->name.c_str());
|
||||
|
||||
ctx->SetDebugPos(pos);
|
||||
ctx->EmitVariableDebugInfo(sym);
|
||||
|
||||
// The various basic blocks that we'll need in the below
|
||||
llvm::BasicBlock *bbFindNext = ctx->CreateBasicBlock("foreach_find_next");
|
||||
llvm::BasicBlock *bbBody = ctx->CreateBasicBlock("foreach_body");
|
||||
llvm::BasicBlock *bbCheckForMore = ctx->CreateBasicBlock("foreach_check_for_more");
|
||||
llvm::BasicBlock *bbDone = ctx->CreateBasicBlock("foreach_done");
|
||||
|
||||
// Prepare the FunctionEmitContext
|
||||
ctx->StartScope();
|
||||
|
||||
// Save the old internal mask so that we can restore it at the end
|
||||
llvm::Value *oldMask = ctx->GetInternalMask();
|
||||
|
||||
// Now, *maskBitsPtr will maintain a bitmask for the lanes that remain
|
||||
// to be processed by a pass through the foreach_unique loop body. It
|
||||
// starts out with the full execution mask (which should never be all
|
||||
// off going in to this)...
|
||||
llvm::Value *oldFullMask = ctx->GetFullMask();
|
||||
llvm::Value *maskBitsPtr = ctx->AllocaInst(LLVMTypes::Int64Type, "mask_bits");
|
||||
llvm::Value *movmsk = ctx->LaneMask(oldFullMask);
|
||||
ctx->StoreInst(movmsk, maskBitsPtr);
|
||||
|
||||
// Officially start the loop; as far as the FunctionEmitContext is
|
||||
// concerned, this can be handled the same way as a regular foreach
|
||||
// loop (continue allowed but not break and return, etc.)
|
||||
ctx->StartForeach();
|
||||
ctx->SetContinueTarget(bbCheckForMore);
|
||||
|
||||
// Evaluate the varying expression we're iterating over just once.
|
||||
llvm::Value *exprValue = expr->GetValue(ctx);
|
||||
|
||||
// And we'll store its value into locally-allocated storage, for ease
|
||||
// of indexing over it with non-compile-time-constant indices.
|
||||
const Type *exprType;
|
||||
llvm::VectorType *llvmExprType;
|
||||
if (exprValue == NULL ||
|
||||
(exprType = expr->GetType()) == NULL ||
|
||||
(llvmExprType =
|
||||
llvm::dyn_cast<llvm::VectorType>(exprValue->getType())) == NULL) {
|
||||
Assert(m->errorCount > 0);
|
||||
return;
|
||||
}
|
||||
ctx->SetDebugPos(pos);
|
||||
const Type *exprPtrType = PointerType::GetUniform(exprType);
|
||||
llvm::Value *exprMem = ctx->AllocaInst(llvmExprType, "expr_mem");
|
||||
ctx->StoreInst(exprValue, exprMem);
|
||||
|
||||
// Onward to find the first set of lanes to run the loop for
|
||||
ctx->BranchInst(bbFindNext);
|
||||
|
||||
ctx->SetCurrentBasicBlock(bbFindNext); {
|
||||
// Load the bitmask of the lanes left to be processed
|
||||
llvm::Value *remainingBits = ctx->LoadInst(maskBitsPtr, "remaining_bits");
|
||||
|
||||
// Find the index of the first set bit in the mask
|
||||
llvm::Function *ctlzFunc =
|
||||
m->module->getFunction("__count_trailing_zeros_i64");
|
||||
Assert(ctlzFunc != NULL);
|
||||
llvm::Value *firstSet = ctx->CallInst(ctlzFunc, NULL, remainingBits,
|
||||
"first_set");
|
||||
|
||||
// And load the corresponding element value from the temporary
|
||||
// memory storing the value of the varying expr.
|
||||
llvm::Value *uniqueValuePtr =
|
||||
ctx->GetElementPtrInst(exprMem, LLVMInt64(0), firstSet, exprPtrType,
|
||||
"unique_index_ptr");
|
||||
llvm::Value *uniqueValue = ctx->LoadInst(uniqueValuePtr, "unique_value");
|
||||
|
||||
// If it's a varying pointer type, need to convert from the int
|
||||
// type we store in the vector to the actual pointer type
|
||||
if (llvm::dyn_cast<llvm::PointerType>(symType) != NULL)
|
||||
uniqueValue = ctx->IntToPtrInst(uniqueValue, symType);
|
||||
|
||||
// Store that value in sym's storage so that the iteration variable
|
||||
// has the right value inside the loop body
|
||||
ctx->StoreInst(uniqueValue, sym->storagePtr);
|
||||
|
||||
// Set the execution mask so that it's on for any lane that a) was
|
||||
// running at the start of the foreach loop, and b) where that
|
||||
// lane's value of the varying expression is the same as the value
|
||||
// we've selected to process this time through--i.e.:
|
||||
// oldMask & (smear(element) == exprValue)
|
||||
llvm::Value *uniqueSmear = ctx->SmearUniform(uniqueValue, "unique_semar");
|
||||
llvm::Value *matchingLanes = NULL;
|
||||
if (uniqueValue->getType()->isFloatingPointTy())
|
||||
matchingLanes =
|
||||
ctx->CmpInst(llvm::Instruction::FCmp, llvm::CmpInst::FCMP_OEQ,
|
||||
uniqueSmear, exprValue, "matching_lanes");
|
||||
else
|
||||
matchingLanes =
|
||||
ctx->CmpInst(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_EQ,
|
||||
uniqueSmear, exprValue, "matching_lanes");
|
||||
matchingLanes = ctx->I1VecToBoolVec(matchingLanes);
|
||||
|
||||
llvm::Value *loopMask =
|
||||
ctx->BinaryOperator(llvm::Instruction::And, oldMask, matchingLanes,
|
||||
"foreach_unique_loop_mask");
|
||||
ctx->SetInternalMask(loopMask);
|
||||
|
||||
// Also update the bitvector of lanes left to process in subsequent
|
||||
// loop iterations:
|
||||
// remainingBits &= ~movmsk(current mask)
|
||||
llvm::Value *loopMaskMM = ctx->LaneMask(loopMask);
|
||||
llvm::Value *notLoopMaskMM = ctx->NotOperator(loopMaskMM);
|
||||
llvm::Value *newRemaining =
|
||||
ctx->BinaryOperator(llvm::Instruction::And, remainingBits,
|
||||
notLoopMaskMM, "new_remaining");
|
||||
ctx->StoreInst(newRemaining, maskBitsPtr);
|
||||
|
||||
// and onward...
|
||||
ctx->BranchInst(bbBody);
|
||||
}
|
||||
|
||||
ctx->SetCurrentBasicBlock(bbBody); {
|
||||
// Run the code in the body of the loop. This is easy now.
|
||||
if (stmts)
|
||||
stmts->EmitCode(ctx);
|
||||
|
||||
Assert(ctx->GetCurrentBasicBlock() != NULL);
|
||||
ctx->BranchInst(bbCheckForMore);
|
||||
}
|
||||
|
||||
ctx->SetCurrentBasicBlock(bbCheckForMore); {
|
||||
// At the end of the loop body (either due to running the
|
||||
// statements normally, or a continue statement in the middle of
|
||||
// the loop that jumps to the end, see if there are any lanes left
|
||||
// to be processed.
|
||||
llvm::Value *remainingBits = ctx->LoadInst(maskBitsPtr, "remaining_bits");
|
||||
llvm::Value *nonZero =
|
||||
ctx->CmpInst(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_NE,
|
||||
remainingBits, LLVMInt64(0), "remaining_ne_zero");
|
||||
ctx->BranchInst(bbFindNext, bbDone, nonZero);
|
||||
}
|
||||
|
||||
ctx->SetCurrentBasicBlock(bbDone);
|
||||
ctx->SetInternalMask(oldMask);
|
||||
ctx->EndForeach();
|
||||
ctx->EndScope();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ForeachUniqueStmt::Print(int indent) const {
|
||||
printf("%*cForeach_unique Stmt", indent, ' ');
|
||||
pos.Print();
|
||||
printf("\n");
|
||||
|
||||
printf("%*cIter symbol: ", indent+4, ' ');
|
||||
if (sym != NULL) {
|
||||
printf("%s", sym->name.c_str());
|
||||
if (sym->type != NULL)
|
||||
printf(" %s", sym->type->GetString().c_str());
|
||||
}
|
||||
else
|
||||
printf("NULL");
|
||||
printf("\n");
|
||||
|
||||
printf("%*cIter expr: ", indent+4, ' ');
|
||||
if (expr != NULL)
|
||||
expr->Print();
|
||||
else
|
||||
printf("NULL");
|
||||
printf("\n");
|
||||
|
||||
printf("%*cStmts:\n", indent+4, ' ');
|
||||
if (stmts != NULL)
|
||||
stmts->Print(indent+8);
|
||||
else
|
||||
printf("NULL");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
Stmt *
|
||||
ForeachUniqueStmt::TypeCheck() {
|
||||
const Type *type;
|
||||
if (sym == NULL || expr == NULL || (type = expr->GetType()) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (type->IsVaryingType() == false) {
|
||||
Error(expr->pos, "Iteration domain type in \"foreach_tiled\" loop "
|
||||
"must be \"varying\" type, not \"%s\".",
|
||||
type->GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Type::IsBasicType(type) == false) {
|
||||
Error(expr->pos, "Iteration domain type in \"foreach_tiled\" loop "
|
||||
"must be an atomic, pointer, or enum type, not \"%s\".",
|
||||
type->GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ForeachUniqueStmt::EstimateCost() const {
|
||||
return COST_VARYING_LOOP;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// CaseStmt
|
||||
|
||||
|
||||
19
stmt.h
19
stmt.h
@@ -260,6 +260,25 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/** Parallel iteration over each unique value in the given (varying)
|
||||
expression.
|
||||
*/
|
||||
class ForeachUniqueStmt : public Stmt {
|
||||
public:
|
||||
ForeachUniqueStmt(const char *iterName, Expr *expr, Stmt *stmts,
|
||||
SourcePos pos);
|
||||
|
||||
void EmitCode(FunctionEmitContext *ctx) const;
|
||||
void Print(int indent) const;
|
||||
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
Symbol *sym;
|
||||
Expr *expr;
|
||||
Stmt *stmts;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Statement implementation for a 'return' or 'coherent' return
|
||||
statement in the program. */
|
||||
|
||||
14
tests/foreach-unique-1.ispc
Normal file
14
tests/foreach-unique-1.ispc
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
uniform int count = 0;
|
||||
float a = aFOO[programIndex];
|
||||
foreach_unique (ua in a)
|
||||
++count;
|
||||
RET[programIndex] = count;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = programCount;
|
||||
}
|
||||
15
tests/foreach-unique-2.ispc
Normal file
15
tests/foreach-unique-2.ispc
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
uniform int count = 0;
|
||||
float a = aFOO[programIndex];
|
||||
// make sure a++ only evaluated once
|
||||
foreach_unique (ua in a++)
|
||||
++count;
|
||||
RET[programIndex] = a;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = programIndex + 2;
|
||||
}
|
||||
15
tests/foreach-unique-3.ispc
Normal file
15
tests/foreach-unique-3.ispc
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
uniform int count = 0;
|
||||
float a = aFOO[programIndex];
|
||||
uniform float sum = 0;
|
||||
foreach_unique (ua in a)
|
||||
sum += ua;
|
||||
RET[programIndex] = sum;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = reduce_add(programIndex+1);
|
||||
}
|
||||
14
tests/foreach-unique-4.ispc
Normal file
14
tests/foreach-unique-4.ispc
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
uniform int count = 0;
|
||||
int a = aFOO[programIndex];
|
||||
foreach_unique (ua in a & 1)
|
||||
++count;
|
||||
RET[programIndex] = count;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 2;
|
||||
}
|
||||
15
tests/foreach-unique-5.ispc
Normal file
15
tests/foreach-unique-5.ispc
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
uniform int count = 0;
|
||||
int a = aFOO[programIndex];
|
||||
if (a & 1)
|
||||
foreach_unique (ua in a & 1)
|
||||
++count;
|
||||
RET[programIndex] = count;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 1;
|
||||
}
|
||||
16
tests/foreach-unique-6.ispc
Normal file
16
tests/foreach-unique-6.ispc
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
uniform int count = 0;
|
||||
float * ptr = &aFOO[programIndex];
|
||||
|
||||
foreach_unique (p in ptr)
|
||||
++count;
|
||||
|
||||
RET[programIndex] = count;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = programCount;
|
||||
}
|
||||
13
tests/foreach-unique-7.ispc
Normal file
13
tests/foreach-unique-7.ispc
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
RET[programIndex] = aFOO[programIndex];
|
||||
|
||||
foreach_unique (p in programIndex & 1)
|
||||
RET[programIndex] = p;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = programIndex & 1;
|
||||
}
|
||||
12
tests_errors/foreach-unique-1.ispc
Normal file
12
tests_errors/foreach-unique-1.ispc
Normal file
@@ -0,0 +1,12 @@
|
||||
// Iteration domain type in "foreach_tiled" loop must be an atomic, pointer, or enum type
|
||||
|
||||
struct Point { float x, y; };
|
||||
|
||||
uniform int foo(Point p) {
|
||||
uniform int count = 0;
|
||||
foreach_unique (pt in p)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
8
tests_errors/foreach-unique-2.ispc
Normal file
8
tests_errors/foreach-unique-2.ispc
Normal file
@@ -0,0 +1,8 @@
|
||||
// Can't assign to type "const uniform int32"
|
||||
|
||||
void foo(int x) {
|
||||
foreach_unique (u in x)
|
||||
++u;
|
||||
}
|
||||
|
||||
|
||||
10
tests_errors/foreach-unique-3.ispc
Normal file
10
tests_errors/foreach-unique-3.ispc
Normal file
@@ -0,0 +1,10 @@
|
||||
// Error: "break" statement is illegal outside of for/while/do loops
|
||||
|
||||
void foo(int x) {
|
||||
foreach_unique (u in x) {
|
||||
if (u == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
tests_errors/foreach-unique-4.ispc
Normal file
10
tests_errors/foreach-unique-4.ispc
Normal file
@@ -0,0 +1,10 @@
|
||||
// Error: "return" statement is illegal inside a "foreach" loop
|
||||
|
||||
void foo(int x) {
|
||||
foreach_unique (u in x) {
|
||||
if (u == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user