From 3bc66136b29a10992d9a891ad4700ad39977bfe6 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Wed, 20 Jun 2012 10:03:44 -0700 Subject: [PATCH] Add foreach_unique iteration construct. Idea via Ingo Wald / IVL compiler. --- ast.cpp | 12 +- docs/ispc.rst | 39 +++++ lex.ll | 9 +- parse.yy | 37 ++++- stmt.cpp | 236 +++++++++++++++++++++++++++++ stmt.h | 19 +++ tests/foreach-unique-1.ispc | 14 ++ tests/foreach-unique-2.ispc | 15 ++ tests/foreach-unique-3.ispc | 15 ++ tests/foreach-unique-4.ispc | 14 ++ tests/foreach-unique-5.ispc | 15 ++ tests/foreach-unique-6.ispc | 16 ++ tests/foreach-unique-7.ispc | 13 ++ tests_errors/foreach-unique-1.ispc | 12 ++ tests_errors/foreach-unique-2.ispc | 8 + tests_errors/foreach-unique-3.ispc | 10 ++ tests_errors/foreach-unique-4.ispc | 10 ++ 17 files changed, 488 insertions(+), 6 deletions(-) create mode 100644 tests/foreach-unique-1.ispc create mode 100644 tests/foreach-unique-2.ispc create mode 100644 tests/foreach-unique-3.ispc create mode 100644 tests/foreach-unique-4.ispc create mode 100644 tests/foreach-unique-5.ispc create mode 100644 tests/foreach-unique-6.ispc create mode 100644 tests/foreach-unique-7.ispc create mode 100644 tests_errors/foreach-unique-1.ispc create mode 100644 tests_errors/foreach-unique-2.ispc create mode 100644 tests_errors/foreach-unique-3.ispc create mode 100644 tests_errors/foreach-unique-4.ispc diff --git a/ast.cpp b/ast.cpp index 96c41616..06ccc1a9 100644 --- a/ast.cpp +++ b/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(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(node)) != NULL) cs->stmts = (Stmt *)WalkAST(cs->stmts, preFunc, postFunc, data); else if ((defs = dynamic_cast(node)) != NULL) @@ -385,12 +390,17 @@ lCheckAllOffSafety(ASTNode *node, void *data) { return false; } - if (dynamic_cast(node) != NULL) { + if (dynamic_cast(node) != NULL || + dynamic_cast(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; } diff --git a/docs/ispc.rst b/docs/ispc.rst index 5d26c93a..1cd98b62 100644 --- a/docs/ispc.rst +++ b/docs/ispc.rst @@ -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" --------------------------------- diff --git a/lex.ll b/lex.ll index ff1e8c25..de08eed2 100644 --- a/lex.ll +++ b/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; } diff --git a/parse.yy b/parse.yy index b1a498f0..d989cf75 100644 --- a/parse.yy +++ b/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 declaration_specifiers %type string_constant -%type struct_or_union_name enum_identifier goto_identifier +%type struct_or_union_name enum_identifier goto_identifier +%type foreach_unique_identifier + %type int_constant soa_width_specifier rate_qualified_new %type 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 diff --git a/stmt.cpp b/stmt.cpp index 25d1d3d5..f4339f6f 100644 --- a/stmt.cpp +++ b/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(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(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 diff --git a/stmt.h b/stmt.h index 88115ab2..01a7244d 100644 --- a/stmt.h +++ b/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. */ diff --git a/tests/foreach-unique-1.ispc b/tests/foreach-unique-1.ispc new file mode 100644 index 00000000..b8319d0b --- /dev/null +++ b/tests/foreach-unique-1.ispc @@ -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; +} diff --git a/tests/foreach-unique-2.ispc b/tests/foreach-unique-2.ispc new file mode 100644 index 00000000..16aa904b --- /dev/null +++ b/tests/foreach-unique-2.ispc @@ -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; +} diff --git a/tests/foreach-unique-3.ispc b/tests/foreach-unique-3.ispc new file mode 100644 index 00000000..63be5653 --- /dev/null +++ b/tests/foreach-unique-3.ispc @@ -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); +} diff --git a/tests/foreach-unique-4.ispc b/tests/foreach-unique-4.ispc new file mode 100644 index 00000000..04957d49 --- /dev/null +++ b/tests/foreach-unique-4.ispc @@ -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; +} diff --git a/tests/foreach-unique-5.ispc b/tests/foreach-unique-5.ispc new file mode 100644 index 00000000..d8074714 --- /dev/null +++ b/tests/foreach-unique-5.ispc @@ -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; +} diff --git a/tests/foreach-unique-6.ispc b/tests/foreach-unique-6.ispc new file mode 100644 index 00000000..6234a77a --- /dev/null +++ b/tests/foreach-unique-6.ispc @@ -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; +} diff --git a/tests/foreach-unique-7.ispc b/tests/foreach-unique-7.ispc new file mode 100644 index 00000000..ec9fcec2 --- /dev/null +++ b/tests/foreach-unique-7.ispc @@ -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; +} diff --git a/tests_errors/foreach-unique-1.ispc b/tests_errors/foreach-unique-1.ispc new file mode 100644 index 00000000..84f1768d --- /dev/null +++ b/tests_errors/foreach-unique-1.ispc @@ -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; +} + + diff --git a/tests_errors/foreach-unique-2.ispc b/tests_errors/foreach-unique-2.ispc new file mode 100644 index 00000000..b3cc0f48 --- /dev/null +++ b/tests_errors/foreach-unique-2.ispc @@ -0,0 +1,8 @@ +// Can't assign to type "const uniform int32" + +void foo(int x) { + foreach_unique (u in x) + ++u; +} + + diff --git a/tests_errors/foreach-unique-3.ispc b/tests_errors/foreach-unique-3.ispc new file mode 100644 index 00000000..5bd6db05 --- /dev/null +++ b/tests_errors/foreach-unique-3.ispc @@ -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; + } +} + + diff --git a/tests_errors/foreach-unique-4.ispc b/tests_errors/foreach-unique-4.ispc new file mode 100644 index 00000000..906ab583 --- /dev/null +++ b/tests_errors/foreach-unique-4.ispc @@ -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; + } +} + +