From cb7ad371c62c6e5bc41991664245e73f8153923b Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Thu, 5 Jan 2012 12:22:05 -0800 Subject: [PATCH 01/60] Run tests using -O2. Disconcertingly, this seems to fix some gcc-only crashes with the generic-16 target (specifically, for half.ispc and for goto-[23].ispc-- those tests run fine with other compilers with generic-16.) --- run_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_tests.py b/run_tests.py index a7da640f..33b60b57 100755 --- a/run_tests.py +++ b/run_tests.py @@ -221,7 +221,7 @@ def run_test(filename): gcc_arch = '-m32' else: gcc_arch = '-m64' - cc_cmd = "%s -msse4.2 -I. %s test_static.cpp -DTEST_SIG=%d %s -o %s" % \ + cc_cmd = "%s -O2 -msse4.2 -I. %s test_static.cpp -DTEST_SIG=%d %s -o %s" % \ (options.compiler_exe, gcc_arch, match, obj_name, exe_name) if platform.system() == 'Darwin': cc_cmd += ' -Wl,-no_pie' From 48e9d4af39fc9c6be5dd6e6bd4fc89e3401fe144 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Thu, 5 Jan 2012 12:21:11 -0800 Subject: [PATCH 02/60] Emit code for #includes in emitted C++ code all at the start of the file. --- cbackend.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/cbackend.cpp b/cbackend.cpp index 41190117..cf606672 100644 --- a/cbackend.cpp +++ b/cbackend.cpp @@ -1741,17 +1741,6 @@ void CWriter::writeOperandWithCast(Value* Operand, const ICmpInst &Cmp) { // static void generateCompilerSpecificCode(formatted_raw_ostream& Out, const TargetData *TD) { - // Alloca, ... - Out << "#include \n" - << "#include \n" - << "/* get a declaration for alloca */\n" - << "#ifdef _MSC_VER\n" - << "#include \n" - << "#define alloca _alloca\n" - << "#else\n" - << "#include \n" - << "#endif\n\n"; - // We output GCC specific attributes to preserve 'linkonce'ness on globals. // If we aren't being compiled with GCC, just drop these attributes. Out << "#ifndef __GNUC__ /* Can only support \"linkonce\" vars with GCC */\n" @@ -1976,7 +1965,6 @@ bool CWriter::doInitialization(Module &M) { Out << " DO NOT EDIT THIS FILE DIRECTLY\n"; Out << " *******************************************************************/\n\n"; - // get declaration for alloca Out << "/* Provide Declarations */\n"; Out << "#include \n"; // Varargs support Out << "#include \n"; // Unwind support @@ -1987,6 +1975,15 @@ bool CWriter::doInitialization(Module &M) { Out << " #define NOMINMAX\n"; Out << " #include \n"; Out << "#endif // _MSC_VER\n"; + Out << "#include \n"; + Out << "#include \n"; + Out << "/* get a declaration for alloca */\n"; + Out << "#ifdef _MSC_VER\n"; + Out << " #include \n"; + Out << " #define alloca _alloca\n"; + Out << "#else\n"; + Out << " #include \n"; + Out << "#endif\n\n"; Out << "#include \"" << includeName << "\"\n"; From 78c6d3c02f327ba930195f4dd7504f5dcf951c1a Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Thu, 5 Jan 2012 12:20:44 -0800 Subject: [PATCH 03/60] Add initial support for 'goto' statements. ispc now supports goto, but only under uniform control flow--i.e. it must be possible for the compiler to statically determine that all program instances will follow the goto. An error is issued at compile time if a goto is used when this is not the case. --- ast.cpp | 9 ++- cbackend.cpp | 2 +- ctx.cpp | 45 +++++++++-- ctx.h | 17 +++++ docs/ispc.txt | 35 ++++++++- examples/intrinsics/generic-16.h | 6 ++ examples/intrinsics/sse4.h | 4 + func.cpp | 13 +++- ispc.h | 3 + parse.yy | 16 +++- stmt.cpp | 127 +++++++++++++++++++++++++++++++ stmt.h | 32 ++++++++ tests/goto-1.ispc | 17 +++++ tests/goto-2.ispc | 18 +++++ tests/goto-3.ispc | 18 +++++ tests/goto-4.ispc | 19 +++++ tests_errors/goto-1.ispc | 10 +++ tests_errors/goto-2.ispc | 11 +++ tests_errors/goto-3.ispc | 11 +++ tests_errors/goto-4.ispc | 10 +++ 20 files changed, 408 insertions(+), 15 deletions(-) create mode 100644 tests/goto-1.ispc create mode 100644 tests/goto-2.ispc create mode 100644 tests/goto-3.ispc create mode 100644 tests/goto-4.ispc create mode 100644 tests_errors/goto-1.ispc create mode 100644 tests_errors/goto-2.ispc create mode 100644 tests_errors/goto-3.ispc create mode 100644 tests_errors/goto-4.ispc diff --git a/ast.cpp b/ast.cpp index 023e4ba9..40503219 100644 --- a/ast.cpp +++ b/ast.cpp @@ -91,6 +91,7 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc, ForStmt *fs; ForeachStmt *fes; ReturnStmt *rs; + LabeledStmt *ls; StmtList *sl; PrintStmt *ps; AssertStmt *as; @@ -131,9 +132,12 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc, fes->stmts = (Stmt *)WalkAST(fes->stmts, preFunc, postFunc, data); } else if (dynamic_cast(node) != NULL || - dynamic_cast(node) != NULL) { - // nothing + dynamic_cast(node) != NULL || + dynamic_cast(node) != NULL) { + // nothing } + else if ((ls = dynamic_cast(node)) != NULL) + ls->stmt = (Stmt *)WalkAST(ls->stmt, preFunc, postFunc, data); else if ((rs = dynamic_cast(node)) != NULL) rs->val = (Expr *)WalkAST(rs->val, preFunc, postFunc, data); else if ((sl = dynamic_cast(node)) != NULL) { @@ -289,3 +293,4 @@ EstimateCost(ASTNode *root) { WalkAST(root, lCostCallback, NULL, &cost); return cost; } + diff --git a/cbackend.cpp b/cbackend.cpp index cf606672..2eb853e3 100644 --- a/cbackend.cpp +++ b/cbackend.cpp @@ -376,7 +376,7 @@ namespace { if (I.getType() == Type::getVoidTy(I.getContext()) || !I.hasOneUse() || isa(I) || isa(I) || isa(I) || isa(I) || isa(I) || isa(I) || - isa(I) || isa(I)) + isa(I) || isa(I) || isa(I)) // Don't inline a load across a store or other bad things! return false; diff --git a/ctx.cpp b/ctx.cpp index 1ad5d086..b0afca64 100644 --- a/ctx.cpp +++ b/ctx.cpp @@ -77,7 +77,7 @@ struct CFInfo { bool IsIf() { return type == If; } bool IsLoop() { return type == Loop; } bool IsForeach() { return type == Foreach; } - bool IsVaryingType() { return !isUniform; } + bool IsVarying() { return !isUniform; } bool IsUniform() { return isUniform; } enum CFType { If, Loop, Foreach }; @@ -157,9 +157,10 @@ CFInfo::GetForeach(llvm::BasicBlock *breakTarget, /////////////////////////////////////////////////////////////////////////// FunctionEmitContext::FunctionEmitContext(Function *func, Symbol *funSym, - llvm::Function *llvmFunction, + llvm::Function *lf, SourcePos firstStmtPos) { function = func; + llvmFunction = lf; /* Create a new basic block to store all of the allocas */ allocaBlock = llvm::BasicBlock::Create(*g->ctx, "allocas", llvmFunction, 0); @@ -762,7 +763,7 @@ int FunctionEmitContext::VaryingCFDepth() const { int sum = 0; for (unsigned int i = 0; i < controlFlowInfo.size(); ++i) - if (controlFlowInfo[i]->IsVaryingType()) + if (controlFlowInfo[i]->IsVarying()) ++sum; return sum; } @@ -777,6 +778,41 @@ FunctionEmitContext::InForeachLoop() const { } +bool +FunctionEmitContext::initLabelBBlocks(ASTNode *node, void *data) { + LabeledStmt *ls = dynamic_cast(node); + if (ls == NULL) + return true; + + FunctionEmitContext *ctx = (FunctionEmitContext *)data; + + if (ctx->labelMap.find(ls->name) != ctx->labelMap.end()) + Error(ls->pos, "Multiple labels named \"%s\" in function.", + ls->name.c_str()); + else { + llvm::BasicBlock *bb = ctx->CreateBasicBlock(ls->name.c_str()); + ctx->labelMap[ls->name] = bb; + } + return true; +} + + +void +FunctionEmitContext::InitializeLabelMap(Stmt *code) { + labelMap.erase(labelMap.begin(), labelMap.end()); + WalkAST(code, initLabelBBlocks, NULL, this); +} + + +llvm::BasicBlock * +FunctionEmitContext::GetLabeledBasicBlock(const std::string &label) { + if (labelMap.find(label) != labelMap.end()) + return labelMap[label]; + else + return NULL; +} + + void FunctionEmitContext::CurrentLanesReturned(Expr *expr, bool doCoherenceCheck) { const Type *returnType = function->GetReturnType(); @@ -920,8 +956,7 @@ FunctionEmitContext::GetStringPtr(const std::string &str) { llvm::BasicBlock * FunctionEmitContext::CreateBasicBlock(const char *name) { - llvm::Function *function = bblock->getParent(); - return llvm::BasicBlock::Create(*g->ctx, name, function); + return llvm::BasicBlock::Create(*g->ctx, name, llvmFunction); } diff --git a/ctx.h b/ctx.h index e573a8ca..743dbb67 100644 --- a/ctx.h +++ b/ctx.h @@ -39,6 +39,7 @@ #define ISPC_CTX_H 1 #include "ispc.h" +#include #include #include #include @@ -192,6 +193,15 @@ public: bool InForeachLoop() const; + /** Step through the code and find label statements; create a basic + block for each one, so that subsequent calls to + GetLabeledBasicBlock() return the corresponding basic block. */ + void InitializeLabelMap(Stmt *code); + + /** If there is a label in the function with the given name, return the + new basic block that it starts. */ + llvm::BasicBlock *GetLabeledBasicBlock(const std::string &label); + /** Called to generate code for 'return' statement; value is the expression in the return statement (if non-NULL), and doCoherenceCheck indicates whether instructions should be generated @@ -446,6 +456,9 @@ private: /** Pointer to the Function for which we're currently generating code. */ Function *function; + /** LLVM function representation for the current function. */ + llvm::Function *llvmFunction; + /** The basic block into which we add any alloca instructions that need to go at the very start of the function. */ llvm::BasicBlock *allocaBlock; @@ -537,6 +550,10 @@ private: tasks launched from the current function. */ llvm::Value *launchGroupHandlePtr; + std::map labelMap; + + static bool initLabelBBlocks(ASTNode *node, void *data); + llvm::Value *pointerVectorToVoidPointers(llvm::Value *value); static void addGSMetadata(llvm::Value *inst, SourcePos pos); bool ifsInLoopAllUniform() const; diff --git a/docs/ispc.txt b/docs/ispc.txt index 011ec208..5d9ea2b0 100644 --- a/docs/ispc.txt +++ b/docs/ispc.txt @@ -100,6 +100,7 @@ Contents: * `Conditional Statements: "if"`_ * `Basic Iteration Statements: "for", "while", and "do"`_ + * `Unstructured Control Flow: "goto"`_ * `"Coherent" Control Flow Statements: "cif" and Friends`_ * `Parallel Iteration Statements: "foreach" and "foreach_tiled"`_ * `Parallel Iteration with "programIndex" and "programCount"`_ @@ -1184,7 +1185,8 @@ but are likely to be supported in future releases: ``int64`` types * Character constants * String constants and arrays of characters as strings -* ``switch`` and ``goto`` statements +* ``switch`` statements +* ``goto`` statements are partially supported (see `Unstructured Control Flow: "goto"`_) * ``union`` types * Bitfield members of ``struct`` types * Variable numbers of arguments to functions @@ -2005,6 +2007,37 @@ one of them executes a ``continue`` statement, other program instances executing code in the loop body that didn't execute the ``continue`` will be unaffected by it. +Unstructured Control Flow: "goto" +--------------------------------- + +``goto`` statements are allowed in ``ispc`` programs under limited +circumstances; specifically, only when the compiler can determine that if +any program instance executes a ``goto`` statement, then all of the program +instances will be running at that statement, such that all will follow the +``goto``. + +Put another way: it's illegal for there to be "varying" control flow +statements in scopes that enclose a ``goto`` statement. An error is issued +if a ``goto`` is used in this situation. + +The syntax for adding labels to ``ispc`` programs and jumping to them with +``goto`` is the same as in C. The following code shows a ``goto`` based +equivalent of a ``for`` loop where the induction variable ``i`` goes from +zero to ten. + +:: + + uniform int i = 0; + check: + if (i > 10) + goto done; + // loop body + ++i; + goto check; + done: + // ... + + "Coherent" Control Flow Statements: "cif" and Friends ----------------------------------------------------- diff --git a/examples/intrinsics/generic-16.h b/examples/intrinsics/generic-16.h index ea120abb..e2426f7f 100644 --- a/examples/intrinsics/generic-16.h +++ b/examples/intrinsics/generic-16.h @@ -307,6 +307,12 @@ static FORCEINLINE uint32_t __movmsk(__vec16_i1 mask) { return mask.v; } +static FORCEINLINE __vec16_i1 __equal(__vec16_i1 a, __vec16_i1 b) { + __vec16_i1 r; + r.v = (a.v & b.v) | (~a.v & ~b.v); + return r; +} + static FORCEINLINE __vec16_i1 __and(__vec16_i1 a, __vec16_i1 b) { __vec16_i1 r; r.v = a.v & b.v; diff --git a/examples/intrinsics/sse4.h b/examples/intrinsics/sse4.h index c9556924..0662a4c0 100644 --- a/examples/intrinsics/sse4.h +++ b/examples/intrinsics/sse4.h @@ -228,6 +228,10 @@ static FORCEINLINE uint32_t __movmsk(__vec4_i1 mask) { return _mm_movemask_ps(mask.v); } +static FORCEINLINE __vec4_i1 __equal(__vec4_i1 a, __vec4_i1 b) { + return _mm_cmpeq_epi32(_mm_castps_si128(a.v), _mm_castps_si128(b.v)); +} + static FORCEINLINE __vec4_i1 __and(__vec4_i1 a, __vec4_i1 b) { return _mm_and_ps(a.v, b.v); } diff --git a/func.cpp b/func.cpp index 288f5e44..6f5e03db 100644 --- a/func.cpp +++ b/func.cpp @@ -290,8 +290,10 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function, llvm::BasicBlock *bbAllOn = ctx->CreateBasicBlock("all_on"); llvm::BasicBlock *bbNotAll = ctx->CreateBasicBlock("not_all_on"); - ctx->BranchInst(bbAllOn, bbNotAll, allOn); + // Set up basic blocks for goto targets + ctx->InitializeLabelMap(code); + ctx->BranchInst(bbAllOn, bbNotAll, allOn); // all on: we've determined dynamically that the mask is all // on. Set the current mask to "all on" explicitly so that // codegen for this path can be improved with this knowledge in @@ -322,12 +324,19 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function, // above ctx->SetCurrentBasicBlock(bbSomeOn); ctx->SetFunctionMask(mask); + + // Set up basic blocks for goto targets again; we want to have + // one set of them for gotos in the 'all on' case, and a + // distinct set for the 'mixed mask' case. + ctx->InitializeLabelMap(code); + code->EmitCode(ctx); if (ctx->GetCurrentBasicBlock()) ctx->ReturnInst(); - } else + // Set up basic blocks for goto targets + ctx->InitializeLabelMap(code); // No check, just emit the code code->EmitCode(ctx); } diff --git a/ispc.h b/ispc.h index 507d3328..e058cc3b 100644 --- a/ispc.h +++ b/ispc.h @@ -98,6 +98,8 @@ namespace llvm { #endif class ArrayType; +class AST; +class ASTNode; class AtomicType; class FunctionEmitContext; class Expr; @@ -421,6 +423,7 @@ enum { COST_FUNPTR_UNIFORM = 12, COST_FUNPTR_VARYING = 24, COST_GATHER = 8, + COST_GOTO = 4, COST_LOAD = 2, COST_REGULAR_BREAK_CONTINUE = 2, COST_RETURN = 4, diff --git a/parse.yy b/parse.yy index 70cb2b3f..e5091e33 100644 --- a/parse.yy +++ b/parse.yy @@ -224,7 +224,7 @@ struct ForeachDimension { %type declaration_specifiers %type string_constant -%type struct_or_union_name enum_identifier +%type struct_or_union_name enum_identifier goto_identifier %type int_constant soa_width_specifier %type foreach_dimension_specifier @@ -1262,7 +1262,11 @@ statement ; labeled_statement - : TOKEN_CASE constant_expression ':' statement + : goto_identifier ':' statement + { + $$ = new LabeledStmt($1, $3, @1); + } + | TOKEN_CASE constant_expression ':' statement { UNIMPLEMENTED; } | TOKEN_DEFAULT ':' statement { UNIMPLEMENTED; } @@ -1433,9 +1437,13 @@ iteration_statement } ; +goto_identifier + : TOKEN_IDENTIFIER { $$ = yylval.stringVal->c_str(); } + ; + jump_statement - : TOKEN_GOTO TOKEN_IDENTIFIER ';' - { UNIMPLEMENTED; } + : TOKEN_GOTO goto_identifier ';' + { $$ = new GotoStmt($2, @1, @2); } | TOKEN_CONTINUE ';' { $$ = new ContinueStmt(false, @1); } | TOKEN_BREAK ';' diff --git a/stmt.cpp b/stmt.cpp index 95142abe..8d46c012 100644 --- a/stmt.cpp +++ b/stmt.cpp @@ -494,6 +494,7 @@ lEmitIfStatements(FunctionEmitContext *ctx, Stmt *stmts, const char *trueOrFalse ctx->EndScope(); } + void IfStmt::EmitCode(FunctionEmitContext *ctx) const { // First check all of the things that might happen due to errors @@ -1915,6 +1916,132 @@ ReturnStmt::Print(int indent) const { } +/////////////////////////////////////////////////////////////////////////// +// GotoStmt + +GotoStmt::GotoStmt(const char *l, SourcePos gotoPos, SourcePos ip) + : Stmt(gotoPos) { + label = l; + identifierPos = ip; +} + + +void +GotoStmt::EmitCode(FunctionEmitContext *ctx) const { + if (ctx->VaryingCFDepth() > 0) { + Error(pos, "\"goto\" statements are only legal under \"uniform\" " + "control flow."); + return; + } + if (ctx->InForeachLoop()) { + Error(pos, "\"goto\" statements are currently illegal inside " + "\"foreach\" loops."); + return; + } + + llvm::BasicBlock *bb = ctx->GetLabeledBasicBlock(label); + if (bb == NULL) { + // TODO: use the string distance stuff to suggest alternatives if + // there are some with names close to the label name we have here.. + Error(identifierPos, "No label named \"%s\" found in current function.", + label.c_str()); + return; + } + + ctx->BranchInst(bb); + ctx->SetCurrentBasicBlock(NULL); +} + + +void +GotoStmt::Print(int indent) const { + printf("%*cGoto label \"%s\"\n", indent, ' ', label.c_str()); +} + + +Stmt * +GotoStmt::Optimize() { + return this; +} + + +Stmt * +GotoStmt::TypeCheck() { + return this; +} + + +int +GotoStmt::EstimateCost() const { + return COST_GOTO; +} + + +/////////////////////////////////////////////////////////////////////////// +// LabeledStmt + +LabeledStmt::LabeledStmt(const char *n, Stmt *s, SourcePos p) + : Stmt(p) { + name = n; + stmt = s; +} + + +void +LabeledStmt::EmitCode(FunctionEmitContext *ctx) const { + llvm::BasicBlock *bblock = ctx->GetLabeledBasicBlock(name); + assert(bblock != NULL); + + // End the current basic block with a jump to our basic block and then + // set things up for emission to continue there. Note that the current + // basic block may validly be NULL going into this statement due to an + // earlier goto that NULLed it out; that doesn't stop us from + // re-establishing a current basic block starting at the label.. + if (ctx->GetCurrentBasicBlock() != NULL) + ctx->BranchInst(bblock); + ctx->SetCurrentBasicBlock(bblock); + + if (stmt != NULL) + stmt->EmitCode(ctx); +} + + +void +LabeledStmt::Print(int indent) const { + printf("%*cLabel \"%s\"\n", indent, ' ', name.c_str()); + if (stmt != NULL) + stmt->Print(indent); +} + + +Stmt * +LabeledStmt::Optimize() { + return this; +} + + +Stmt * +LabeledStmt::TypeCheck() { + if (!isalpha(name[0]) || name[0] == '_') { + Error(pos, "Label must start with either alphabetic character or '_'."); + return NULL; + } + for (unsigned int i = 1; i < name.size(); ++i) { + if (!isalnum(name[i]) && name[i] != '_') { + Error(pos, "Character \"%c\" is illegal in labels.", name[i]); + return NULL; + } + } + return this; +} + + +int +LabeledStmt::EstimateCost() const { + return 0; +} + + /////////////////////////////////////////////////////////////////////////// // StmtList diff --git a/stmt.h b/stmt.h index 73142197..eb5af2f9 100644 --- a/stmt.h +++ b/stmt.h @@ -282,6 +282,38 @@ public: }; +class GotoStmt : public Stmt { +public: + GotoStmt(const char *label, SourcePos gotoPos, SourcePos idPos); + + void EmitCode(FunctionEmitContext *ctx) const; + void Print(int indent) const; + + Stmt *Optimize(); + Stmt *TypeCheck(); + int EstimateCost() const; + + std::string label; + SourcePos identifierPos; +}; + + +class LabeledStmt : public Stmt { +public: + LabeledStmt(const char *label, Stmt *stmt, SourcePos p); + + void EmitCode(FunctionEmitContext *ctx) const; + void Print(int indent) const; + + Stmt *Optimize(); + Stmt *TypeCheck(); + int EstimateCost() const; + + std::string name; + Stmt *stmt; +}; + + /** @brief Representation of a list of statements in the program. */ class StmtList : public Stmt { diff --git a/tests/goto-1.ispc b/tests/goto-1.ispc new file mode 100644 index 00000000..dd7ab119 --- /dev/null +++ b/tests/goto-1.ispc @@ -0,0 +1,17 @@ + +export uniform int width() { return programCount; } + + +export void f_f(uniform float RET[], uniform float aFOO[]) { + float a = aFOO[programIndex]; + float b = 0.; b = a; + RET[programIndex] = a+b; + goto skip; + RET[programIndex] = 0; + skip: + ; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 2 + 2*programIndex; +} diff --git a/tests/goto-2.ispc b/tests/goto-2.ispc new file mode 100644 index 00000000..d2b1f8b1 --- /dev/null +++ b/tests/goto-2.ispc @@ -0,0 +1,18 @@ + +export uniform int width() { return programCount; } + + +export void f_f(uniform float RET[], uniform float aFOO[]) { + float a = aFOO[programIndex]; + float b = 0.; b = a; + RET[programIndex] = a+b; + if (all(a != 0)) + goto skip; + RET[programIndex] = 0; + skip: + ; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 2 + 2*programIndex; +} diff --git a/tests/goto-3.ispc b/tests/goto-3.ispc new file mode 100644 index 00000000..bcad69ad --- /dev/null +++ b/tests/goto-3.ispc @@ -0,0 +1,18 @@ + +export uniform int width() { return programCount; } + + +export void f_f(uniform float RET[], uniform float aFOO[]) { + float a = aFOO[programIndex]; + float b = 0.; b = a; + RET[programIndex] = a+b; + if (all(a == 0)) + goto skip; + RET[programIndex] = 0; + skip: + ; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 0; +} diff --git a/tests/goto-4.ispc b/tests/goto-4.ispc new file mode 100644 index 00000000..97c373ef --- /dev/null +++ b/tests/goto-4.ispc @@ -0,0 +1,19 @@ + +export uniform int width() { return programCount; } + + +export void f_f(uniform float RET[], uniform float aFOO[]) { + float a = aFOO[programIndex]; + float b = 0.; b = a; + RET[programIndex] = 0; + encore: + ++RET[programIndex]; + if (any(a != 0)) { + a = max(a-1, 0); + goto encore; + } +} + +export void result(uniform float RET[]) { + RET[programIndex] = programCount+1; +} diff --git a/tests_errors/goto-1.ispc b/tests_errors/goto-1.ispc new file mode 100644 index 00000000..dbe14491 --- /dev/null +++ b/tests_errors/goto-1.ispc @@ -0,0 +1,10 @@ +// Multiple labels named "label" in function + +void func(int x) { + label: + ; + label: + ; +} + + diff --git a/tests_errors/goto-2.ispc b/tests_errors/goto-2.ispc new file mode 100644 index 00000000..fc4a00c6 --- /dev/null +++ b/tests_errors/goto-2.ispc @@ -0,0 +1,11 @@ +// "goto" statements are only legal under "uniform" control flow + +void func(int x) { + if (x < 0) + goto label; + + label: + ; +} + + diff --git a/tests_errors/goto-3.ispc b/tests_errors/goto-3.ispc new file mode 100644 index 00000000..2a1dc48c --- /dev/null +++ b/tests_errors/goto-3.ispc @@ -0,0 +1,11 @@ +// "goto" statements are only legal under "uniform" control flow + +void func(int x) { + cif (x < 0) + goto label; + + label: + ; +} + + diff --git a/tests_errors/goto-4.ispc b/tests_errors/goto-4.ispc new file mode 100644 index 00000000..08a84b4d --- /dev/null +++ b/tests_errors/goto-4.ispc @@ -0,0 +1,10 @@ +// "goto" statements are only legal under "uniform" control flow + +void func(int x) { + label: + + for(int i =0 ;i Date: Fri, 6 Jan 2012 08:41:01 -0800 Subject: [PATCH 04/60] Fix bug where we'd sometimes inadvertently lose cv-qualifiers on pointers. Fixes issue #142. --- parse.yy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/parse.yy b/parse.yy index e5091e33..3d85c0f7 100644 --- a/parse.yy +++ b/parse.yy @@ -1112,8 +1112,7 @@ type_name abstract_declarator : pointer { - Declarator *d = new Declarator(DK_POINTER, @1); - $$ = d; + $$ = $1; } | direct_abstract_declarator | pointer direct_abstract_declarator From 71317e6aa6c280c09ea1c69cc1766315cb498c7e Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Fri, 6 Jan 2012 09:19:18 -0800 Subject: [PATCH 05/60] Fix bug in gather/scatter optimization passes. When flattening chains of insertelement instructions, we didn't handle the case where the initial insertelement was to a constant vector (with one value set and the other values undef). Also generalized the "do all of the instances access the same location" check to handle the case where some of them are accessing undef locations; these are ignored in this check, as they should correspond to the mask being off for that lane anyway. Fixes issue #149. --- opt.cpp | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/opt.cpp b/opt.cpp index 0685509f..b30efd10 100644 --- a/opt.cpp +++ b/opt.cpp @@ -957,8 +957,16 @@ lFlattenInsertChain(llvm::InsertElementInst *ie, int vectorWidth, llvm::Value *insertBase = ie->getOperand(0); ie = llvm::dyn_cast(insertBase); - if (ie == NULL) - Assert(llvm::isa(insertBase)); + if (ie == NULL) { + if (llvm::isa(insertBase)) + return; + + llvm::ConstantVector *cv = + llvm::dyn_cast(insertBase); + Assert(cv != NULL); + Assert(iOffset < (int)cv->getNumOperands()); + elements[iOffset] = cv->getOperand(iOffset); + } } } @@ -1884,19 +1892,27 @@ lVectorValuesAllEqual(llvm::Value *v, int vectorLength, llvm::Value *elements[ISPC_MAX_NVEC]; lFlattenInsertChain(ie, vectorLength, elements); - for (int i = 0; i < vectorLength-1; ++i) { - // TODO: It's not clear what to do in this case (which - // corresponds to elements of the vector being undef). It is - // probably to just ignore undef elements and return true if - // all of the other ones are equal, but it'd be nice to have - // some test cases to verify this. - Assert(elements[i] != NULL && elements[i+1] != NULL); + // We will ignore any values of elements[] that are NULL; as they + // correspond to undefined values--we just want to see if all of + // the defined values have the same value. + int lastNonNull = 0; + while (lastNonNull < vectorLength && elements[lastNonNull] == NULL) + ++lastNonNull; + + if (lastNonNull == vectorLength) + // all of them are undef! + return true; + + for (int i = lastNonNull; i < vectorLength; ++i) { + if (elements[i] == NULL) + continue; std::vector seenPhi0; std::vector seenPhi1; - if (lValuesAreEqual(elements[i], elements[i+1], seenPhi0, + if (lValuesAreEqual(elements[lastNonNull], elements[i], seenPhi0, seenPhi1) == false) return false; + lastNonNull = i; } return true; } From 15cc812e37b4dd3b44c3a4594c94990498caeec6 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Fri, 6 Jan 2012 11:52:58 -0800 Subject: [PATCH 06/60] Add notion of "unbound" variability to the type system. Now, when a type is declared without an explicit "uniform" or "varying" qualifier, its variability is unbound; depending on the context of the declaration, the variability is later finalized. Currently, in almost all cases, types with unbound variability are resolved to varying types; the one exception is typecasts like: "(int)1"; in this case, the fact that (int) has unbound variability carries through to the TypeCastExpr, which in turn notices that the expression being type cast has uniform type and in turn will resolve (int) to (uniform int). Fixes issue #127. --- decl.cpp | 128 +++-- decl.h | 4 +- expr.cpp | 34 +- expr.h | 4 +- main.cpp | 3 + parse.yy | 59 +-- stmt.cpp | 9 +- tests_errors/fptr-typecheck-2.ispc | 2 +- type.cpp | 737 +++++++++++++++++++++-------- type.h | 151 ++++-- 10 files changed, 782 insertions(+), 349 deletions(-) diff --git a/decl.cpp b/decl.cpp index 59c79302..41ff8bac 100644 --- a/decl.cpp +++ b/decl.cpp @@ -46,6 +46,18 @@ #include #include +static void +lPrintTypeQualifiers(int typeQualifiers) { + if (typeQualifiers & TYPEQUAL_INLINE) printf("inline "); + if (typeQualifiers & TYPEQUAL_CONST) printf("const "); + if (typeQualifiers & TYPEQUAL_UNIFORM) printf("uniform "); + if (typeQualifiers & TYPEQUAL_VARYING) printf("varying "); + if (typeQualifiers & TYPEQUAL_TASK) printf("task "); + if (typeQualifiers & TYPEQUAL_SIGNED) printf("signed "); + if (typeQualifiers & TYPEQUAL_UNSIGNED) printf("unsigned "); +} + + /** Given a Type and a set of type qualifiers, apply the type qualifiers to the type, returning the type that is the result. */ @@ -54,6 +66,16 @@ lApplyTypeQualifiers(int typeQualifiers, const Type *type, SourcePos pos) { if (type == NULL) return NULL; + if ((typeQualifiers & TYPEQUAL_CONST) != 0) + type = type->GetAsConstType(); + + if ((typeQualifiers & TYPEQUAL_UNIFORM) != 0) + type = type->GetAsUniformType(); + else if ((typeQualifiers & TYPEQUAL_VARYING) != 0) + type = type->GetAsVaryingType(); + else + type = type->GetAsUnboundVariabilityType(); + if ((typeQualifiers & TYPEQUAL_UNSIGNED) != 0) { if ((typeQualifiers & TYPEQUAL_SIGNED) != 0) Error(pos, "Illegal to apply both \"signed\" and \"unsigned\" " @@ -64,29 +86,13 @@ lApplyTypeQualifiers(int typeQualifiers, const Type *type, SourcePos pos) { type = unsignedType; else Error(pos, "\"unsigned\" qualifier is illegal with \"%s\" type.", - type->GetString().c_str()); - + type->ResolveUnboundVariability(Type::Varying)->GetString().c_str()); } if ((typeQualifiers & TYPEQUAL_SIGNED) != 0 && type->IsIntType() == false) Error(pos, "\"signed\" qualifier is illegal with non-integer type " - "\"%s\".", type->GetString().c_str()); - - if ((typeQualifiers & TYPEQUAL_CONST) != 0) - type = type->GetAsConstType(); - - if ((typeQualifiers & TYPEQUAL_UNIFORM) != 0) - type = type->GetAsUniformType(); - else if ((typeQualifiers & TYPEQUAL_VARYING) != 0) - type = type->GetAsVaryingType(); - else { - // otherwise, structs are uniform by default and everything - // else is varying by default - if (dynamic_cast(type->GetBaseType()) != NULL) - type = type->GetAsUniformType(); - else - type = type->GetAsVaryingType(); - } + "\"%s\".", + type->ResolveUnboundVariability(Type::Varying)->GetString().c_str()); return type; } @@ -138,21 +144,14 @@ lGetStorageClassName(StorageClass storageClass) { void DeclSpecs::Print() const { - printf("%s ", lGetStorageClassName(storageClass)); + printf("Declspecs: [%s ", lGetStorageClassName(storageClass)); if (soaWidth > 0) printf("soa<%d> ", soaWidth); - - if (typeQualifiers & TYPEQUAL_INLINE) printf("inline "); - if (typeQualifiers & TYPEQUAL_CONST) printf("const "); - if (typeQualifiers & TYPEQUAL_UNIFORM) printf("uniform "); - if (typeQualifiers & TYPEQUAL_VARYING) printf("varying "); - if (typeQualifiers & TYPEQUAL_TASK) printf("task "); - if (typeQualifiers & TYPEQUAL_SIGNED) printf("signed "); - if (typeQualifiers & TYPEQUAL_UNSIGNED) printf("unsigned "); - - printf("%s", baseType->GetString().c_str()); + lPrintTypeQualifiers(typeQualifiers); + printf("base type: %s", baseType->GetString().c_str()); if (vectorSize > 0) printf("<%d>", vectorSize); + printf("]"); } @@ -192,19 +191,46 @@ Declarator::GetSymbol() const { void -Declarator::Print() const { +Declarator::Print(int indent) const { + printf("%*cdeclarator: [", indent, ' '); + pos.Print(); + + lPrintTypeQualifiers(typeQualifiers); Symbol *sym = GetSymbol(); if (sym != NULL) printf("%s", sym->name.c_str()); else printf("(null symbol)"); + printf(", array size = %d", arraySize); + + printf(", kind = "); + switch (kind) { + case DK_BASE: printf("base"); break; + case DK_POINTER: printf("pointer"); break; + case DK_REFERENCE: printf("reference"); break; + case DK_ARRAY: printf("array"); break; + case DK_FUNCTION: printf("function"); break; + default: FATAL("Unhandled declarator kind"); + } + if (initExpr != NULL) { printf(" = ("); initExpr->Print(); printf(")"); } - pos.Print(); + + if (functionParams.size() > 0) { + for (unsigned int i = 0; i < functionParams.size(); ++i) { + printf("\n%*cfunc param %d:\n", indent, ' ', i); + functionParams[i]->Print(indent+4); + } + } + + if (child != NULL) + child->Print(indent + 4); + + printf("]\n"); } @@ -237,9 +263,14 @@ Declarator::GetFunctionInfo(DeclSpecs *ds, std::vector *funArgs) { for (unsigned int i = 0; i < d->functionParams.size(); ++i) { Declaration *pdecl = d->functionParams[i]; Assert(pdecl->declarators.size() == 1); - funArgs->push_back(pdecl->declarators[0]->GetSymbol()); + + Symbol *sym = pdecl->declarators[0]->GetSymbol(); + sym->type = sym->type->ResolveUnboundVariability(Type::Varying); + funArgs->push_back(sym); } + funSym->type = funSym->type->ResolveUnboundVariability(Type::Varying); + return funSym; } @@ -258,6 +289,12 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { if (kind != DK_FUNCTION && isTask) Error(pos, "\"task\" qualifier illegal in variable declaration."); + Type::Variability variability = Type::Unbound; + if (hasUniformQual) + variability = Type::Uniform; + else if (hasVaryingQual) + variability = Type::Varying; + const Type *type = base; switch (kind) { case DK_BASE: @@ -268,7 +305,7 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { return type; case DK_POINTER: - type = new PointerType(type, hasUniformQual, isConst); + type = new PointerType(type, variability, isConst); if (child != NULL) return child->GetType(type, ds); else @@ -397,7 +434,7 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { Error(pos, "No return type provided in function declaration."); return NULL; } - + bool isExported = ds && (ds->storageClass == SC_EXPORT); bool isExternC = ds && (ds->storageClass == SC_EXTERN_C); bool isTask = ds && ((ds->typeQualifiers & TYPEQUAL_TASK) != 0); @@ -418,9 +455,10 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { return NULL; } - Type *functionType = - new FunctionType(returnType, args, pos, argNames, argDefaults, + const Type *functionType = + new FunctionType(returnType, args, argNames, argDefaults, argPos, isTask, isExported, isExternC); + functionType = functionType->ResolveUnboundVariability(Type::Varying); return child->GetType(functionType, ds); } default: @@ -497,6 +535,8 @@ Declaration::GetVariableDeclarations() const { continue; Symbol *sym = decl->GetSymbol(); + sym->type = sym->type->ResolveUnboundVariability(Type::Varying); + if (dynamic_cast(sym->type) != NULL) { // function declaration m->symbolTable->AddFunction(sym); @@ -511,14 +551,12 @@ Declaration::GetVariableDeclarations() const { void -Declaration::Print() const { - printf("Declaration: specs ["); +Declaration::Print(int indent) const { + printf("%*cDeclaration: specs [", indent, ' '); declSpecs->Print(); - printf("], declarators ["); - for (unsigned int i = 0 ; i < declarators.size(); ++i) { - declarators[i]->Print(); - printf("%s", (i == declarators.size() - 1) ? "]" : ", "); - } + printf("], declarators:\n"); + for (unsigned int i = 0 ; i < declarators.size(); ++i) + declarators[i]->Print(indent+4); } /////////////////////////////////////////////////////////////////////////// @@ -539,7 +577,7 @@ GetStructTypesNamesPositions(const std::vector &sd, DeclSpecs ds(type); if (type->IsUniformType()) ds.typeQualifiers |= TYPEQUAL_UNIFORM; - else + else if (type->IsVaryingType()) ds.typeQualifiers |= TYPEQUAL_VARYING; for (unsigned int j = 0; j < sd[i]->declarators->size(); ++j) { diff --git a/decl.h b/decl.h index cd6f34b8..ebeee9a4 100644 --- a/decl.h +++ b/decl.h @@ -156,7 +156,7 @@ public: /** Returns the symbol associated with the declarator. */ Symbol *GetSymbol() const; - void Print() const; + void Print(int indent) const; /** Position of the declarator in the source program. */ const SourcePos pos; @@ -199,7 +199,7 @@ public: Declaration(DeclSpecs *ds, std::vector *dlist = NULL); Declaration(DeclSpecs *ds, Declarator *d); - void Print() const; + void Print(int indent) const; /** This method walks through all of the Declarators in a declaration and returns a fully-initialized Symbol and (possibly) and diff --git a/expr.cpp b/expr.cpp index af63de9d..2fd3145f 100644 --- a/expr.cpp +++ b/expr.cpp @@ -36,6 +36,7 @@ */ #include "expr.h" +#include "ast.h" #include "type.h" #include "sym.h" #include "ctx.h" @@ -224,7 +225,7 @@ lDoTypeConv(const Type *fromType, const Type *toType, Expr **expr, eltType = eltType->GetAsConstType(); if (Type::Equal(toPointerType, new PointerType(eltType, - toPointerType->IsUniformType(), + toPointerType->GetVariability(), toPointerType->IsConstType()))) goto typecast_ok; else { @@ -466,7 +467,7 @@ lDoTypeConv(const Type *fromType, const Type *toType, Expr **expr, typecast_ok: if (expr != NULL) - *expr = new TypeCastExpr(toType, *expr, false, pos); + *expr = new TypeCastExpr(toType, *expr, pos); return true; } @@ -2709,7 +2710,7 @@ FunctionCallExpr::TypeCheck() { !(argCouldBeNULL[i] == true && dynamic_cast(paramType) != NULL)) { Error(args->exprs[i]->pos, "Can't convert argument of " - "type \"%s\" to type \"%s\" for funcion call " + "type \"%s\" to type \"%s\" for function call " "argument.", argTypes[i]->GetString().c_str(), paramType->GetString().c_str()); return NULL; @@ -3525,6 +3526,12 @@ VectorMemberExpr::getElementType() const { MemberExpr * MemberExpr::create(Expr *e, const char *id, SourcePos p, SourcePos idpos, bool derefLValue) { + // FIXME: we need to call TypeCheck() here so that we can call + // e->GetType() in the following. But really we just shouldn't try to + // resolve this now but just have a generic MemberExpr type that + // handles all cases so that this is unnecessary. + e = ::TypeCheck(e); + const Type *exprType; if (e == NULL || (exprType = e->GetType()) == NULL) return NULL; @@ -4566,11 +4573,10 @@ ConstExpr::Print() const { /////////////////////////////////////////////////////////////////////////// // TypeCastExpr -TypeCastExpr::TypeCastExpr(const Type *t, Expr *e, bool pu, SourcePos p) +TypeCastExpr::TypeCastExpr(const Type *t, Expr *e, SourcePos p) : Expr(p) { type = t; expr = e; - preserveUniformity = pu; } @@ -5213,7 +5219,7 @@ TypeCastExpr::GetValue(FunctionEmitContext *ctx) const { if (Type::EqualIgnoringConst(arrayAsPtr->GetType(), toPointerType) == false) { Assert(Type::EqualIgnoringConst(arrayAsPtr->GetType()->GetAsVaryingType(), toPointerType) == true); - arrayAsPtr = new TypeCastExpr(toPointerType, arrayAsPtr, false, pos); + arrayAsPtr = new TypeCastExpr(toPointerType, arrayAsPtr, pos); arrayAsPtr = ::TypeCheck(arrayAsPtr); Assert(arrayAsPtr != NULL); arrayAsPtr = ::Optimize(arrayAsPtr); @@ -5364,6 +5370,7 @@ TypeCastExpr::GetValue(FunctionEmitContext *ctx) const { const Type * TypeCastExpr::GetType() const { + Assert(type->HasUnboundVariability() == false); return type; } @@ -5373,7 +5380,7 @@ lDeconstifyType(const Type *t) { const PointerType *pt = dynamic_cast(t); if (pt != NULL) return new PointerType(lDeconstifyType(pt->GetBaseType()), - pt->IsUniformType(), false); + pt->GetVariability(), false); else return t->GetAsNonConstType(); } @@ -5384,16 +5391,16 @@ TypeCastExpr::TypeCheck() { if (expr == NULL) return NULL; - const Type *toType = GetType(), *fromType = expr->GetType(); + const Type *toType = type, *fromType = expr->GetType(); if (toType == NULL || fromType == NULL) return NULL; - if (preserveUniformity == true && fromType->IsUniformType() && - toType->IsVaryingType()) { + if (toType->HasUnboundVariability() && fromType->IsUniformType()) { TypeCastExpr *tce = new TypeCastExpr(toType->GetAsUniformType(), - expr, false, pos); + expr, pos); return ::TypeCheck(tce); } + type = toType = type->ResolveUnboundVariability(Type::Varying); fromType = lDeconstifyType(fromType); toType = lDeconstifyType(toType); @@ -5862,6 +5869,8 @@ SizeOfExpr::SizeOfExpr(Expr *e, SourcePos p) SizeOfExpr::SizeOfExpr(const Type *t, SourcePos p) : Expr(p), expr(NULL), type(t) { + if (type->HasUnboundVariability()) + type = type->ResolveUnboundVariability(Type::Varying); } @@ -6026,7 +6035,8 @@ FunctionSymbolExpr::GetType() const { return NULL; } - return matchingFunc ? new PointerType(matchingFunc->type, true, true) : NULL; + return matchingFunc ? + new PointerType(matchingFunc->type, Type::Uniform, true) : NULL; } diff --git a/expr.h b/expr.h index 3c290c8a..c66b7754 100644 --- a/expr.h +++ b/expr.h @@ -493,8 +493,7 @@ private: probably-different type. */ class TypeCastExpr : public Expr { public: - TypeCastExpr(const Type *t, Expr *e, bool preserveUniformity, - SourcePos p); + TypeCastExpr(const Type *t, Expr *e, SourcePos p); llvm::Value *GetValue(FunctionEmitContext *ctx) const; const Type *GetType() const; @@ -507,7 +506,6 @@ public: const Type *type; Expr *expr; - bool preserveUniformity; }; diff --git a/main.cpp b/main.cpp index 357e7532..84c5c142 100644 --- a/main.cpp +++ b/main.cpp @@ -38,6 +38,7 @@ #include "ispc.h" #include "module.h" #include "util.h" +#include "type.h" #include #include #include @@ -188,6 +189,8 @@ int main(int Argc, char *Argv[]) { LLVMInitializeX86TargetMC(); #endif + AtomicType::Init(); + char *file = NULL; const char *headerFileName = NULL; const char *outFileName = NULL; diff --git a/parse.yy b/parse.yy index 3d85c0f7..97cc6eff 100644 --- a/parse.yy +++ b/parse.yy @@ -362,13 +362,7 @@ cast_expression : unary_expression | '(' type_name ')' cast_expression { - // Pass true here to try to preserve uniformity - // so that things like: - // uniform int y = ...; - // uniform float x = 1. / (float)y; - // don't issue an error due to (float)y being inadvertently - // and undesirably-to-the-user "varying"... - $$ = new TypeCastExpr($2, $4, true, Union(@1,@4)); + $$ = new TypeCastExpr($2, $4, Union(@1,@4)); } ; @@ -638,13 +632,13 @@ type_specifier atomic_var_type_specifier : TOKEN_VOID { $$ = AtomicType::Void; } - | TOKEN_BOOL { $$ = AtomicType::VaryingBool; } - | TOKEN_INT8 { $$ = AtomicType::VaryingInt8; } - | TOKEN_INT16 { $$ = AtomicType::VaryingInt16; } - | TOKEN_INT { $$ = AtomicType::VaryingInt32; } - | TOKEN_FLOAT { $$ = AtomicType::VaryingFloat; } - | TOKEN_DOUBLE { $$ = AtomicType::VaryingDouble; } - | TOKEN_INT64 { $$ = AtomicType::VaryingInt64; } + | TOKEN_BOOL { $$ = AtomicType::UnboundBool; } + | TOKEN_INT8 { $$ = AtomicType::UnboundInt8; } + | TOKEN_INT16 { $$ = AtomicType::UnboundInt16; } + | TOKEN_INT { $$ = AtomicType::UnboundInt32; } + | TOKEN_FLOAT { $$ = AtomicType::UnboundFloat; } + | TOKEN_DOUBLE { $$ = AtomicType::UnboundDouble; } + | TOKEN_INT64 { $$ = AtomicType::UnboundInt64; } ; short_vec_specifier @@ -670,7 +664,7 @@ struct_or_union_specifier GetStructTypesNamesPositions(*$4, &elementTypes, &elementNames, &elementPositions); StructType *st = new StructType($2, elementTypes, elementNames, - elementPositions, false, true, @2); + elementPositions, false, Type::Unbound, @2); m->symbolTable->AddType($2, st, @2); $$ = st; } @@ -681,8 +675,9 @@ struct_or_union_specifier std::vector elementPositions; GetStructTypesNamesPositions(*$3, &elementTypes, &elementNames, &elementPositions); + // FIXME: should be unbound $$ = new StructType("", elementTypes, elementNames, elementPositions, - false, true, @1); + false, Type::Unbound, @1); } | struct_or_union '{' '}' { @@ -748,7 +743,7 @@ specifier_qualifier_list else if ($1 == TYPEQUAL_SIGNED) { if ($2->IsIntType() == false) { Error(@1, "Can't apply \"signed\" qualifier to \"%s\" type.", - $2->GetString().c_str()); + $2->ResolveUnboundVariability(Type::Varying)->GetString().c_str()); $$ = $2; } } @@ -758,7 +753,7 @@ specifier_qualifier_list $$ = t; else { Error(@1, "Can't apply \"unsigned\" qualifier to \"%s\" type. Ignoring.", - $2->GetString().c_str()); + $2->ResolveUnboundVariability(Type::Varying)->GetString().c_str()); $$ = $2; } } @@ -775,8 +770,11 @@ specifier_qualifier_list else FATAL("Unhandled type qualifier in parser."); } - else + else { + if (m->errorCount == 0) + Error(@1, "Lost type qualifier in parser."); $$ = NULL; + } } ; @@ -1558,19 +1556,21 @@ lAddDeclaration(DeclSpecs *ds, Declarator *decl) { const Type *t = decl->GetType(ds); if (t == NULL) return; + + Symbol *sym = decl->GetSymbol(); + Assert(sym != NULL); const FunctionType *ft = dynamic_cast(t); if (ft != NULL) { - Symbol *funSym = decl->GetSymbol(); - Assert(funSym != NULL); - funSym->type = ft; - funSym->storageClass = ds->storageClass; - + sym->type = ft; + sym->storageClass = ds->storageClass; bool isInline = (ds->typeQualifiers & TYPEQUAL_INLINE); - m->AddFunctionDeclaration(funSym, isInline); + m->AddFunctionDeclaration(sym, isInline); + } + else { + sym->type = sym->type->ResolveUnboundVariability(Type::Varying); + bool isConst = (ds->typeQualifiers & TYPEQUAL_CONST) != 0; + m->AddGlobalVariable(sym, decl->initExpr, isConst); } - else - m->AddGlobalVariable(decl->GetSymbol(), decl->initExpr, - (ds->typeQualifiers & TYPEQUAL_CONST) != 0); } } @@ -1596,6 +1596,7 @@ lAddFunctionParams(Declarator *decl) { continue; Assert(pdecl->declarators.size() == 1); Symbol *sym = pdecl->declarators[0]->GetSymbol(); + sym->type = sym->type->ResolveUnboundVariability(Type::Varying); #ifndef NDEBUG bool ok = m->symbolTable->AddVariable(sym); if (ok == false) @@ -1761,7 +1762,7 @@ lFinalizeEnumeratorSymbols(std::vector &enums, the actual enum type here and optimize it, which will have us end up with a ConstExpr with the desired EnumType... */ Expr *castExpr = new TypeCastExpr(enumType, enums[i]->constValue, - false, enums[i]->pos); + enums[i]->pos); castExpr = Optimize(castExpr); enums[i]->constValue = dynamic_cast(castExpr); Assert(enums[i]->constValue != NULL); diff --git a/stmt.cpp b/stmt.cpp index 8d46c012..5a803366 100644 --- a/stmt.cpp +++ b/stmt.cpp @@ -1133,7 +1133,7 @@ DoStmt::TypeCheck() { !lHasVaryingBreakOrContinue(bodyStmts)); testExpr = new TypeCastExpr(uniformTest ? AtomicType::UniformBool : AtomicType::VaryingBool, - testExpr, false, testExpr->pos); + testExpr, testExpr->pos); } return this; @@ -1318,8 +1318,7 @@ ForStmt::TypeCheck() { !g->opt.disableUniformControlFlow && !lHasVaryingBreakOrContinue(stmts)); test = new TypeCastExpr(uniformTest ? AtomicType::UniformBool : - AtomicType::VaryingBool, - test, false, test->pos); + AtomicType::VaryingBool, test, test->pos); test = ::TypeCheck(test); if (test == NULL) return NULL; @@ -2147,7 +2146,7 @@ lProcessPrintArg(Expr *expr, FunctionEmitContext *ctx, std::string &argTypes) { baseType == AtomicType::UniformUInt16) { expr = new TypeCastExpr(type->IsUniformType() ? AtomicType::UniformInt32 : AtomicType::VaryingInt32, - expr, false, expr->pos); + expr, expr->pos); type = expr->GetType(); } @@ -2350,7 +2349,7 @@ AssertStmt::TypeCheck() { } expr = new TypeCastExpr(isUniform ? AtomicType::UniformBool : AtomicType::VaryingBool, - expr, false, expr->pos); + expr, expr->pos); expr = ::TypeCheck(expr); } return this; diff --git a/tests_errors/fptr-typecheck-2.ispc b/tests_errors/fptr-typecheck-2.ispc index 13071fcd..3769b301 100644 --- a/tests_errors/fptr-typecheck-2.ispc +++ b/tests_errors/fptr-typecheck-2.ispc @@ -1,4 +1,4 @@ -// Can't convert argument of type "void * const uniform" to type "float" for funcion call argument. +// Can't convert argument of type "void * uniform" to type "float" for function call argument. float bar(float a, float b); diff --git a/type.cpp b/type.cpp index 5502d039..b5f7ad5a 100644 --- a/type.cpp +++ b/type.cpp @@ -70,63 +70,243 @@ lShouldPrintName(const std::string &name) { /////////////////////////////////////////////////////////////////////////// // AtomicType -const AtomicType *AtomicType::UniformBool = new AtomicType(TYPE_BOOL, true, false); -const AtomicType *AtomicType::VaryingBool = new AtomicType(TYPE_BOOL, false, false); -const AtomicType *AtomicType::UniformInt8 = new AtomicType(TYPE_INT8, true, false); -const AtomicType *AtomicType::VaryingInt8 = new AtomicType(TYPE_INT8, false, false); -const AtomicType *AtomicType::UniformUInt8 = new AtomicType(TYPE_UINT8, true, false); -const AtomicType *AtomicType::VaryingUInt8 = new AtomicType(TYPE_UINT8, false, false); -const AtomicType *AtomicType::UniformInt16 = new AtomicType(TYPE_INT16, true, false); -const AtomicType *AtomicType::VaryingInt16 = new AtomicType(TYPE_INT16, false, false); -const AtomicType *AtomicType::UniformUInt16 = new AtomicType(TYPE_UINT16, true, false); -const AtomicType *AtomicType::VaryingUInt16 = new AtomicType(TYPE_UINT16, false, false); -const AtomicType *AtomicType::UniformInt32 = new AtomicType(TYPE_INT32, true, false); -const AtomicType *AtomicType::VaryingInt32 = new AtomicType(TYPE_INT32, false, false); -const AtomicType *AtomicType::UniformUInt32 = new AtomicType(TYPE_UINT32, true, false); -const AtomicType *AtomicType::VaryingUInt32 = new AtomicType(TYPE_UINT32, false, false); -const AtomicType *AtomicType::UniformFloat = new AtomicType(TYPE_FLOAT, true, false); -const AtomicType *AtomicType::VaryingFloat = new AtomicType(TYPE_FLOAT, false, false); -const AtomicType *AtomicType::UniformInt64 = new AtomicType(TYPE_INT64, true, false); -const AtomicType *AtomicType::VaryingInt64 = new AtomicType(TYPE_INT64, false, false); -const AtomicType *AtomicType::UniformUInt64 = new AtomicType(TYPE_UINT64, true, false); -const AtomicType *AtomicType::VaryingUInt64 = new AtomicType(TYPE_UINT64, false, false); -const AtomicType *AtomicType::UniformDouble = new AtomicType(TYPE_DOUBLE, true, false); -const AtomicType *AtomicType::VaryingDouble = new AtomicType(TYPE_DOUBLE, false, false); +// All of the details of the layout of this array are used implicitly in +// the AtomicType implementation below; reoder it with care! For example, +// the fact that for signed integer types, the unsigned equivalent integer +// type follows in the next major array element is used in the routine to +// get unsigned types. -const AtomicType *AtomicType::UniformConstBool = new AtomicType(TYPE_BOOL, true, true); -const AtomicType *AtomicType::VaryingConstBool = new AtomicType(TYPE_BOOL, false, true); -const AtomicType *AtomicType::UniformConstInt8 = new AtomicType(TYPE_INT8, true, true); -const AtomicType *AtomicType::VaryingConstInt8 = new AtomicType(TYPE_INT8, false, true); -const AtomicType *AtomicType::UniformConstUInt8 = new AtomicType(TYPE_UINT8, true, true); -const AtomicType *AtomicType::VaryingConstUInt8 = new AtomicType(TYPE_UINT8, false, true); -const AtomicType *AtomicType::UniformConstInt16 = new AtomicType(TYPE_INT16, true, true); -const AtomicType *AtomicType::VaryingConstInt16 = new AtomicType(TYPE_INT16, false, true); -const AtomicType *AtomicType::UniformConstUInt16 = new AtomicType(TYPE_UINT16, true, true); -const AtomicType *AtomicType::VaryingConstUInt16 = new AtomicType(TYPE_UINT16, false, true); -const AtomicType *AtomicType::UniformConstInt32 = new AtomicType(TYPE_INT32, true, true); -const AtomicType *AtomicType::VaryingConstInt32 = new AtomicType(TYPE_INT32, false, true); -const AtomicType *AtomicType::UniformConstUInt32 = new AtomicType(TYPE_UINT32, true, true); -const AtomicType *AtomicType::VaryingConstUInt32 = new AtomicType(TYPE_UINT32, false, true); -const AtomicType *AtomicType::UniformConstFloat = new AtomicType(TYPE_FLOAT, true, true); -const AtomicType *AtomicType::VaryingConstFloat = new AtomicType(TYPE_FLOAT, false, true); -const AtomicType *AtomicType::UniformConstInt64 = new AtomicType(TYPE_INT64, true, true); -const AtomicType *AtomicType::VaryingConstInt64 = new AtomicType(TYPE_INT64, false, true); -const AtomicType *AtomicType::UniformConstUInt64 = new AtomicType(TYPE_UINT64, true, true); -const AtomicType *AtomicType::VaryingConstUInt64 = new AtomicType(TYPE_UINT64, false, true); -const AtomicType *AtomicType::UniformConstDouble = new AtomicType(TYPE_DOUBLE, true, true); -const AtomicType *AtomicType::VaryingConstDouble = new AtomicType(TYPE_DOUBLE, false, true); +const AtomicType *AtomicType::typeTable[AtomicType::NUM_BASIC_TYPES][3][2] = { + { { NULL, NULL }, {NULL, NULL}, {NULL,NULL} }, /* NULL type */ + { { new AtomicType(AtomicType::TYPE_BOOL, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_BOOL, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_BOOL, Type::Varying, false), + new AtomicType(AtomicType::TYPE_BOOL, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_BOOL, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_BOOL, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_INT8, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_INT8, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_INT8, Type::Varying, false), + new AtomicType(AtomicType::TYPE_INT8, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_INT8, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_INT8, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_UINT8, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_UINT8, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_UINT8, Type::Varying, false), + new AtomicType(AtomicType::TYPE_UINT8, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_UINT8, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_UINT8, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_INT16, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_INT16, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_INT16, Type::Varying, false), + new AtomicType(AtomicType::TYPE_INT16, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_INT16, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_INT16, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_UINT16, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_UINT16, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_UINT16, Type::Varying, false), + new AtomicType(AtomicType::TYPE_UINT16, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_UINT16, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_UINT16, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_INT32, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_INT32, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_INT32, Type::Varying, false), + new AtomicType(AtomicType::TYPE_INT32, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_INT32, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_INT32, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_UINT32, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_UINT32, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_UINT32, Type::Varying, false), + new AtomicType(AtomicType::TYPE_UINT32, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_UINT32, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_UINT32, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_FLOAT, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_FLOAT, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_FLOAT, Type::Varying, false), + new AtomicType(AtomicType::TYPE_FLOAT, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_FLOAT, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_FLOAT, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_INT64, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_INT64, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_INT64, Type::Varying, false), + new AtomicType(AtomicType::TYPE_INT64, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_INT64, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_INT64, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_UINT64, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_UINT64, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_UINT64, Type::Varying, false), + new AtomicType(AtomicType::TYPE_UINT64, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_UINT64, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_UINT64, Type::Unbound, true), } }, + { { new AtomicType(AtomicType::TYPE_DOUBLE, Type::Uniform, false), + new AtomicType(AtomicType::TYPE_DOUBLE, Type::Uniform, true), }, + { new AtomicType(AtomicType::TYPE_DOUBLE, Type::Varying, false), + new AtomicType(AtomicType::TYPE_DOUBLE, Type::Varying, true), }, + { new AtomicType(AtomicType::TYPE_DOUBLE, Type::Unbound, false), + new AtomicType(AtomicType::TYPE_DOUBLE, Type::Unbound, true), } } +}; -const AtomicType *AtomicType::Void = new AtomicType(TYPE_VOID, true, false); +const AtomicType *AtomicType::UniformBool; +const AtomicType *AtomicType::VaryingBool; +const AtomicType *AtomicType::UnboundBool; + +const AtomicType *AtomicType::UniformInt8; +const AtomicType *AtomicType::VaryingInt8; +const AtomicType *AtomicType::UnboundInt8; + +const AtomicType *AtomicType::UniformUInt8; +const AtomicType *AtomicType::VaryingUInt8; +const AtomicType *AtomicType::UnboundUInt8; + +const AtomicType *AtomicType::UniformInt16; +const AtomicType *AtomicType::VaryingInt16; +const AtomicType *AtomicType::UnboundInt16; + +const AtomicType *AtomicType::UniformUInt16; +const AtomicType *AtomicType::VaryingUInt16; +const AtomicType *AtomicType::UnboundUInt16; + +const AtomicType *AtomicType::UniformInt32; +const AtomicType *AtomicType::VaryingInt32; +const AtomicType *AtomicType::UnboundInt32; + +const AtomicType *AtomicType::UniformUInt32; +const AtomicType *AtomicType::VaryingUInt32; +const AtomicType *AtomicType::UnboundUInt32; + +const AtomicType *AtomicType::UniformFloat; +const AtomicType *AtomicType::VaryingFloat; +const AtomicType *AtomicType::UnboundFloat; + +const AtomicType *AtomicType::UniformInt64; +const AtomicType *AtomicType::VaryingInt64; +const AtomicType *AtomicType::UnboundInt64; + +const AtomicType *AtomicType::UniformUInt64; +const AtomicType *AtomicType::VaryingUInt64; +const AtomicType *AtomicType::UnboundUInt64; + +const AtomicType *AtomicType::UniformDouble; +const AtomicType *AtomicType::VaryingDouble; +const AtomicType *AtomicType::UnboundDouble; -AtomicType::AtomicType(BasicType bt, bool iu, bool ic) - : basicType(bt), isUniform(iu), isConst(ic) { +const AtomicType *AtomicType::UniformConstBool; +const AtomicType *AtomicType::VaryingConstBool; +const AtomicType *AtomicType::UnboundConstBool; +const AtomicType *AtomicType::UniformConstInt8; +const AtomicType *AtomicType::VaryingConstInt8; +const AtomicType *AtomicType::UnboundConstInt8; +const AtomicType *AtomicType::UniformConstUInt8; +const AtomicType *AtomicType::VaryingConstUInt8; +const AtomicType *AtomicType::UnboundConstUInt8; +const AtomicType *AtomicType::UniformConstInt16; +const AtomicType *AtomicType::VaryingConstInt16; +const AtomicType *AtomicType::UnboundConstInt16; +const AtomicType *AtomicType::UniformConstUInt16; +const AtomicType *AtomicType::VaryingConstUInt16; +const AtomicType *AtomicType::UnboundConstUInt16; +const AtomicType *AtomicType::UniformConstInt32; +const AtomicType *AtomicType::VaryingConstInt32; +const AtomicType *AtomicType::UnboundConstInt32; +const AtomicType *AtomicType::UniformConstUInt32; +const AtomicType *AtomicType::VaryingConstUInt32; +const AtomicType *AtomicType::UnboundConstUInt32; +const AtomicType *AtomicType::UniformConstFloat; +const AtomicType *AtomicType::VaryingConstFloat; +const AtomicType *AtomicType::UnboundConstFloat; +const AtomicType *AtomicType::UniformConstInt64; +const AtomicType *AtomicType::VaryingConstInt64; +const AtomicType *AtomicType::UnboundConstInt64; +const AtomicType *AtomicType::UniformConstUInt64; +const AtomicType *AtomicType::VaryingConstUInt64; +const AtomicType *AtomicType::UnboundConstUInt64; +const AtomicType *AtomicType::UniformConstDouble; +const AtomicType *AtomicType::VaryingConstDouble; +const AtomicType *AtomicType::UnboundConstDouble; + +const AtomicType *AtomicType::Void = new AtomicType(TYPE_VOID, Type::Uniform, false); + + +void AtomicType::Init() { + UniformBool = typeTable[TYPE_BOOL][Type::Uniform][0]; + VaryingBool = typeTable[TYPE_BOOL][Type::Varying][0]; + UnboundBool = typeTable[TYPE_BOOL][Type::Unbound][0]; + UniformInt8 = typeTable[TYPE_INT8][Type::Uniform][0]; + VaryingInt8 = typeTable[TYPE_INT8][Type::Varying][0]; + UnboundInt8 = typeTable[TYPE_INT8][Type::Unbound][0]; + UniformUInt8 = typeTable[TYPE_UINT8][Type::Uniform][0]; + VaryingUInt8 = typeTable[TYPE_UINT8][Type::Varying][0]; + UnboundUInt8 = typeTable[TYPE_UINT8][Type::Unbound][0]; + UniformInt16 = typeTable[TYPE_INT16][Type::Uniform][0]; + VaryingInt16 = typeTable[TYPE_INT16][Type::Varying][0]; + UnboundInt16 = typeTable[TYPE_INT16][Type::Unbound][0]; + UniformUInt16 = typeTable[TYPE_UINT16][Type::Uniform][0]; + VaryingUInt16 = typeTable[TYPE_UINT16][Type::Varying][0]; + UnboundUInt16 = typeTable[TYPE_UINT16][Type::Unbound][0]; + UniformInt32 = typeTable[TYPE_INT32][Type::Uniform][0]; + VaryingInt32 = typeTable[TYPE_INT32][Type::Varying][0]; + UnboundInt32 = typeTable[TYPE_INT32][Type::Unbound][0]; + UniformUInt32 = typeTable[TYPE_UINT32][Type::Uniform][0]; + VaryingUInt32 = typeTable[TYPE_UINT32][Type::Varying][0]; + UnboundUInt32 = typeTable[TYPE_UINT32][Type::Unbound][0]; + UniformFloat = typeTable[TYPE_FLOAT][Type::Uniform][0]; + VaryingFloat = typeTable[TYPE_FLOAT][Type::Varying][0]; + UnboundFloat = typeTable[TYPE_FLOAT][Type::Unbound][0]; + UniformInt64 = typeTable[TYPE_INT64][Type::Uniform][0]; + VaryingInt64 = typeTable[TYPE_INT64][Type::Varying][0]; + UnboundInt64 = typeTable[TYPE_INT64][Type::Unbound][0]; + UniformUInt64 = typeTable[TYPE_UINT64][Type::Uniform][0]; + VaryingUInt64 = typeTable[TYPE_UINT64][Type::Varying][0]; + UnboundUInt64 = typeTable[TYPE_UINT64][Type::Unbound][0]; + UniformDouble = typeTable[TYPE_DOUBLE][Type::Uniform][0]; + VaryingDouble = typeTable[TYPE_DOUBLE][Type::Varying][0]; + UnboundDouble = typeTable[TYPE_DOUBLE][Type::Unbound][0]; + + UniformConstBool = typeTable[TYPE_BOOL][Type::Uniform][1]; + VaryingConstBool = typeTable[TYPE_BOOL][Type::Varying][1]; + UnboundConstBool = typeTable[TYPE_BOOL][Type::Unbound][1]; + UniformConstInt8 = typeTable[TYPE_INT8][Type::Uniform][1]; + VaryingConstInt8 = typeTable[TYPE_INT8][Type::Varying][1]; + UnboundConstInt8 = typeTable[TYPE_INT8][Type::Unbound][1]; + UniformConstUInt8 = typeTable[TYPE_UINT8][Type::Uniform][1]; + VaryingConstUInt8 = typeTable[TYPE_UINT8][Type::Varying][1]; + UnboundConstUInt8 = typeTable[TYPE_UINT8][Type::Unbound][1]; + UniformConstInt16 = typeTable[TYPE_INT16][Type::Uniform][1]; + VaryingConstInt16 = typeTable[TYPE_INT16][Type::Varying][1]; + UnboundConstInt16 = typeTable[TYPE_INT16][Type::Unbound][1]; + UniformConstUInt16 = typeTable[TYPE_UINT16][Type::Uniform][1]; + VaryingConstUInt16 = typeTable[TYPE_UINT16][Type::Varying][1]; + UnboundConstUInt16 = typeTable[TYPE_UINT16][Type::Unbound][1]; + UniformConstInt32 = typeTable[TYPE_INT32][Type::Uniform][1]; + VaryingConstInt32 = typeTable[TYPE_INT32][Type::Varying][1]; + UnboundConstInt32 = typeTable[TYPE_INT32][Type::Unbound][1]; + UniformConstUInt32 = typeTable[TYPE_UINT32][Type::Uniform][1]; + VaryingConstUInt32 = typeTable[TYPE_UINT32][Type::Varying][1]; + UnboundConstUInt32 = typeTable[TYPE_UINT32][Type::Unbound][1]; + UniformConstFloat = typeTable[TYPE_FLOAT][Type::Uniform][1]; + VaryingConstFloat = typeTable[TYPE_FLOAT][Type::Varying][1]; + UnboundConstFloat = typeTable[TYPE_FLOAT][Type::Unbound][1]; + UniformConstInt64 = typeTable[TYPE_INT64][Type::Uniform][1]; + VaryingConstInt64 = typeTable[TYPE_INT64][Type::Varying][1]; + UnboundConstInt64 = typeTable[TYPE_INT64][Type::Unbound][1]; + UniformConstUInt64 = typeTable[TYPE_UINT64][Type::Uniform][1]; + VaryingConstUInt64 = typeTable[TYPE_UINT64][Type::Varying][1]; + UnboundConstUInt64 = typeTable[TYPE_UINT64][Type::Unbound][1]; + UniformConstDouble = typeTable[TYPE_DOUBLE][Type::Uniform][1]; + VaryingConstDouble = typeTable[TYPE_DOUBLE][Type::Varying][1]; + UnboundConstDouble = typeTable[TYPE_DOUBLE][Type::Unbound][1]; } -bool -AtomicType::IsUniformType() const { - return isUniform; +AtomicType::AtomicType(BasicType bt, Variability v, bool ic) + : basicType(bt), variability(v), isConst(ic) { +} + + +Type::Variability +AtomicType::GetVariability() const { + return variability; } @@ -166,26 +346,13 @@ AtomicType::IsConstType() const { const AtomicType * AtomicType::GetAsUnsignedType() const { - if (IsUnsignedType()) + if (IsUnsignedType() == true) return this; - if (this == AtomicType::UniformInt8) return AtomicType::UniformUInt8; - else if (this == AtomicType::VaryingInt8) return AtomicType::VaryingUInt8; - else if (this == AtomicType::UniformInt16) return AtomicType::UniformUInt16; - else if (this == AtomicType::VaryingInt16) return AtomicType::VaryingUInt16; - else if (this == AtomicType::UniformInt32) return AtomicType::UniformUInt32; - else if (this == AtomicType::VaryingInt32) return AtomicType::VaryingUInt32; - else if (this == AtomicType::UniformInt64) return AtomicType::UniformUInt64; - else if (this == AtomicType::VaryingInt64) return AtomicType::VaryingUInt64; - else if (this == AtomicType::UniformConstInt8) return AtomicType::UniformConstUInt8; - else if (this == AtomicType::VaryingConstInt8) return AtomicType::VaryingConstUInt8; - else if (this == AtomicType::UniformConstInt16) return AtomicType::UniformConstUInt16; - else if (this == AtomicType::VaryingConstInt16) return AtomicType::VaryingConstUInt16; - else if (this == AtomicType::UniformConstInt32) return AtomicType::UniformConstUInt32; - else if (this == AtomicType::VaryingConstInt32) return AtomicType::VaryingConstUInt32; - else if (this == AtomicType::UniformConstInt64) return AtomicType::UniformConstUInt64; - else if (this == AtomicType::VaryingConstInt64) return AtomicType::VaryingConstUInt64; - else return NULL; + if (IsIntType() == false) + return NULL; + + return typeTable[basicType + 1][variability][isConst ? 1 : 0]; } @@ -193,23 +360,8 @@ const AtomicType * AtomicType::GetAsConstType() const { if (this == AtomicType::Void) return this; - - switch (basicType) { - case TYPE_BOOL: return isUniform ? UniformConstBool : VaryingConstBool; - case TYPE_INT8: return isUniform ? UniformConstInt8 : VaryingConstInt8; - case TYPE_UINT8: return isUniform ? UniformConstUInt8 : VaryingConstUInt8; - case TYPE_INT16: return isUniform ? UniformConstInt16 : VaryingConstInt16; - case TYPE_UINT16: return isUniform ? UniformConstUInt16 : VaryingConstUInt16; - case TYPE_INT32: return isUniform ? UniformConstInt32 : VaryingConstInt32; - case TYPE_UINT32: return isUniform ? UniformConstUInt32 : VaryingConstUInt32; - case TYPE_FLOAT: return isUniform ? UniformConstFloat : VaryingConstFloat; - case TYPE_INT64: return isUniform ? UniformConstInt64 : VaryingConstInt64; - case TYPE_UINT64: return isUniform ? UniformConstUInt64 : VaryingConstUInt64; - case TYPE_DOUBLE: return isUniform ? UniformConstDouble : VaryingConstDouble; - default: - FATAL("logic error in AtomicType::GetAsConstType()"); - return NULL; - } + + return typeTable[basicType][variability][1]; } @@ -218,22 +370,7 @@ AtomicType::GetAsNonConstType() const { if (this == AtomicType::Void) return this; - switch (basicType) { - case TYPE_BOOL: return isUniform ? UniformBool : VaryingBool; - case TYPE_INT8: return isUniform ? UniformInt8 : VaryingInt8; - case TYPE_UINT8: return isUniform ? UniformUInt8 : VaryingUInt8; - case TYPE_INT16: return isUniform ? UniformInt16 : VaryingInt16; - case TYPE_UINT16: return isUniform ? UniformUInt16 : VaryingUInt16; - case TYPE_INT32: return isUniform ? UniformInt32 : VaryingInt32; - case TYPE_UINT32: return isUniform ? UniformUInt32 : VaryingUInt32; - case TYPE_FLOAT: return isUniform ? UniformFloat : VaryingFloat; - case TYPE_INT64: return isUniform ? UniformInt64 : VaryingInt64; - case TYPE_UINT64: return isUniform ? UniformUInt64 : VaryingUInt64; - case TYPE_DOUBLE: return isUniform ? UniformDouble : VaryingDouble; - default: - FATAL("logic error in AtomicType::GetAsNonConstType()"); - return NULL; - } + return typeTable[basicType][variability][0]; } @@ -245,49 +382,34 @@ AtomicType::GetBaseType() const { const AtomicType * AtomicType::GetAsVaryingType() const { - if (IsVaryingType()) + if (this == AtomicType::Void) return this; - - switch (basicType) { - case TYPE_VOID: return this; - case TYPE_BOOL: return isConst ? VaryingConstBool : VaryingBool; - case TYPE_INT8: return isConst ? VaryingConstInt8 : VaryingInt8; - case TYPE_UINT8: return isConst ? VaryingConstUInt8 : VaryingUInt8; - case TYPE_INT16: return isConst ? VaryingConstInt16 : VaryingInt16; - case TYPE_UINT16: return isConst ? VaryingConstUInt16 : VaryingUInt16; - case TYPE_INT32: return isConst ? VaryingConstInt32 : VaryingInt32; - case TYPE_UINT32: return isConst ? VaryingConstUInt32 : VaryingUInt32; - case TYPE_FLOAT: return isConst ? VaryingConstFloat : VaryingFloat; - case TYPE_INT64: return isConst ? VaryingConstInt64 : VaryingInt64; - case TYPE_UINT64: return isConst ? VaryingConstUInt64 : VaryingUInt64; - case TYPE_DOUBLE: return isConst ? VaryingConstDouble : VaryingDouble; - default: FATAL("Logic error in AtomicType::GetAsVaryingType()"); - } - return NULL; + return typeTable[basicType][Varying][isConst ? 1 : 0]; } const AtomicType * AtomicType::GetAsUniformType() const { - if (IsUniformType()) + if (this == AtomicType::Void) return this; + return typeTable[basicType][Uniform][isConst ? 1 : 0]; +} - switch (basicType) { - case TYPE_VOID: return this; - case TYPE_BOOL: return isConst ? UniformConstBool : UniformBool; - case TYPE_INT8: return isConst ? UniformConstInt8 : UniformInt8; - case TYPE_UINT8: return isConst ? UniformConstUInt8 : UniformUInt8; - case TYPE_INT16: return isConst ? UniformConstInt16 : UniformInt16; - case TYPE_UINT16: return isConst ? UniformConstUInt16 : UniformUInt16; - case TYPE_INT32: return isConst ? UniformConstInt32 : UniformInt32; - case TYPE_UINT32: return isConst ? UniformConstUInt32 : UniformUInt32; - case TYPE_FLOAT: return isConst ? UniformConstFloat : UniformFloat; - case TYPE_INT64: return isConst ? UniformConstInt64 : UniformInt64; - case TYPE_UINT64: return isConst ? UniformConstUInt64 : UniformUInt64; - case TYPE_DOUBLE: return isConst ? UniformConstDouble : UniformDouble; - default: FATAL("Logic error in AtomicType::GetAsUniformType()"); - } - return NULL; + +const AtomicType * +AtomicType::GetAsUnboundVariabilityType() const { + if (this == AtomicType::Void) + return this; + return typeTable[basicType][Unbound][isConst ? 1 : 0]; +} + + +const AtomicType * +AtomicType::ResolveUnboundVariability(Variability v) const { + Assert(v != Unbound); + if (variability != Unbound) + return this; + return typeTable[basicType][v][isConst ? 1 : 0]; } @@ -303,7 +425,11 @@ AtomicType::GetString() const { std::string ret; if (basicType != TYPE_VOID) { if (isConst) ret += "const "; - if (isUniform) ret += "uniform "; + switch (variability) { + case Uniform: ret += "uniform "; break; + case Varying: /*ret += "varying ";*/ break; + case Unbound: ret += "/*unbound*/ "; break; + } } switch (basicType) { @@ -329,7 +455,12 @@ std::string AtomicType::Mangle() const { std::string ret; if (isConst) ret += "C"; - if (isUniform) ret += "U"; + switch (variability) { + case Uniform: ret += "uf"; break; + case Varying: ret += "vy"; break; + case Unbound: FATAL("Variability shoudln't be unbound in call to " + "AtomicType::Mangle()."); + } switch (basicType) { case TYPE_VOID: ret += "v"; break; @@ -353,7 +484,7 @@ AtomicType::Mangle() const { std::string AtomicType::GetCDeclaration(const std::string &name) const { std::string ret; - if (isUniform == false) { + if (variability != Uniform) { Assert(m->errorCount > 0); return ret; } @@ -385,6 +516,9 @@ AtomicType::GetCDeclaration(const std::string &name) const { LLVM_TYPE_CONST llvm::Type * AtomicType::LLVMType(llvm::LLVMContext *ctx) const { + Assert(variability != Unbound); + bool isUniform = (variability == Uniform); + switch (basicType) { case TYPE_VOID: return llvm::Type::getVoidTy(*ctx); @@ -415,7 +549,8 @@ AtomicType::LLVMType(llvm::LLVMContext *ctx) const { llvm::DIType AtomicType::GetDIType(llvm::DIDescriptor scope) const { - if (isUniform) { + Assert(variability != Unbound); + if (variability == Uniform) { switch (basicType) { case TYPE_VOID: return llvm::DIType(); @@ -491,20 +626,20 @@ EnumType::EnumType(SourcePos p) : pos(p) { // name = "/* (anonymous) */"; isConst = false; - isUniform = false; + variability = Unbound; } EnumType::EnumType(const char *n, SourcePos p) : pos(p), name(n) { isConst = false; - isUniform = false; + variability = Unbound; } -bool -EnumType::IsUniformType() const { - return isUniform; +Type::Variability +EnumType::GetVariability() const { + return variability; } @@ -545,24 +680,48 @@ EnumType::GetBaseType() const { const EnumType * -EnumType::GetAsVaryingType() const { - if (IsVaryingType()) +EnumType::GetAsUniformType() const { + if (IsUniformType()) return this; else { EnumType *enumType = new EnumType(*this); - enumType->isUniform = false; + enumType->variability = Uniform; return enumType; } } const EnumType * -EnumType::GetAsUniformType() const { - if (IsUniformType()) +EnumType::ResolveUnboundVariability(Variability v) const { + if (variability == v || variability != Unbound) return this; else { EnumType *enumType = new EnumType(*this); - enumType->isUniform = true; + enumType->variability = v; + return enumType; + } +} + + +const EnumType * +EnumType::GetAsVaryingType() const { + if (IsVaryingType()) + return this; + else { + EnumType *enumType = new EnumType(*this); + enumType->variability = Varying; + return enumType; + } +} + + +const EnumType * +EnumType::GetAsUnboundVariabilityType() const { + if (HasUnboundVariability()) + return this; + else { + EnumType *enumType = new EnumType(*this); + enumType->variability = Unbound; return enumType; } } @@ -603,7 +762,13 @@ std::string EnumType::GetString() const { std::string ret; if (isConst) ret += "const "; - if (isUniform) ret += "uniform "; + + switch (variability) { + case Uniform: ret += "uniform "; break; + case Varying: /*ret += "varying ";*/ break; + case Unbound: ret += "/*unbound*/ "; break; + } + ret += "enum "; if (name.size()) ret += name; @@ -613,13 +778,25 @@ EnumType::GetString() const { std::string EnumType::Mangle() const { - std::string ret = std::string("enum[") + name + std::string("]"); + std::string ret; + + Assert(variability != Unbound); + if (variability == Uniform) ret += "uf"; + else ret += "vy"; + + ret += std::string("enum[") + name + std::string("]"); + return ret; } std::string EnumType::GetCDeclaration(const std::string &varName) const { + if (variability != Uniform) { + Assert(m->errorCount > 0); + return ""; + } + std::string ret; if (isConst) ret += "const "; ret += "enum"; @@ -635,12 +812,15 @@ EnumType::GetCDeclaration(const std::string &varName) const { LLVM_TYPE_CONST llvm::Type * EnumType::LLVMType(llvm::LLVMContext *ctx) const { - return isUniform ? LLVMTypes::Int32Type : LLVMTypes::Int32VectorType; + Assert(variability != Unbound); + return (variability == Uniform) ? LLVMTypes::Int32Type : + LLVMTypes::Int32VectorType; } llvm::DIType EnumType::GetDIType(llvm::DIDescriptor scope) const { + Assert(variability != Unbound); std::vector enumeratorDescriptors; for (unsigned int i = 0; i < enumerators.size(); ++i) { unsigned int enumeratorValue; @@ -704,23 +884,23 @@ EnumType::GetEnumerator(int i) const { /////////////////////////////////////////////////////////////////////////// // PointerType -PointerType *PointerType::Void = new PointerType(AtomicType::Void, true, true); +PointerType *PointerType::Void = new PointerType(AtomicType::Void, Uniform, false); -PointerType::PointerType(const Type *t, bool iu, bool ic) - : isUniform(iu), isConst(ic) { +PointerType::PointerType(const Type *t, Variability v, bool ic) + : variability(v), isConst(ic) { baseType = t; } PointerType * PointerType::GetUniform(const Type *t) { - return new PointerType(t, true, false); + return new PointerType(t, Uniform, false); } PointerType * PointerType::GetVarying(const Type *t) { - return new PointerType(t, false, false); + return new PointerType(t, Varying, false); } @@ -731,9 +911,9 @@ PointerType::IsVoidPointer(const Type *t) { } -bool -PointerType::IsUniformType() const { - return isUniform; +Type::Variability +PointerType::GetVariability() const { + return variability; } @@ -775,19 +955,36 @@ PointerType::GetBaseType() const { const PointerType * PointerType::GetAsVaryingType() const { - if (isUniform == false) + if (variability == Varying) return this; else - return new PointerType(baseType, false, isConst); + return new PointerType(baseType, Varying, isConst); } const PointerType * PointerType::GetAsUniformType() const { - if (isUniform == true) + if (variability == Uniform) return this; else - return new PointerType(baseType, true, isConst); + return new PointerType(baseType, Uniform, isConst); +} + + +const PointerType * +PointerType::GetAsUnboundVariabilityType() const { + if (variability == Unbound) + return this; + else + return new PointerType(baseType, Unbound, isConst); +} + + +const PointerType * +PointerType::ResolveUnboundVariability(Variability v) const { + return new PointerType(baseType->ResolveUnboundVariability(v), + (variability == Unbound) ? v : variability, + isConst); } @@ -803,7 +1000,7 @@ PointerType::GetAsConstType() const { if (isConst == true) return this; else - return new PointerType(baseType, isUniform, true); + return new PointerType(baseType, variability, true); } @@ -812,7 +1009,7 @@ PointerType::GetAsNonConstType() const { if (isConst == false) return this; else - return new PointerType(baseType, isUniform, false); + return new PointerType(baseType, variability, false); } @@ -825,23 +1022,34 @@ PointerType::GetString() const { ret += std::string(" *"); if (isConst) ret += " const"; - if (isUniform) ret += " uniform"; + switch (variability) { + case Uniform: ret += " uniform"; break; + case Varying: /*ret += " varying";*/ break; + case Unbound: ret += " /*unbound*/"; break; + } + return ret; } std::string PointerType::Mangle() const { + Assert(variability != Unbound); if (baseType == NULL) return ""; - return (isUniform ? std::string("uptr<") : std::string("vptr<")) + + return ((variability == Uniform) ? std::string("uptr<") : std::string("vptr<")) + baseType->Mangle() + std::string(">"); } std::string PointerType::GetCDeclaration(const std::string &name) const { + if (variability != Uniform) { + Assert(m->errorCount > 0); + return ""; + } + if (baseType == NULL) return ""; @@ -856,10 +1064,13 @@ PointerType::GetCDeclaration(const std::string &name) const { LLVM_TYPE_CONST llvm::Type * PointerType::LLVMType(llvm::LLVMContext *ctx) const { + Assert(variability != Unbound); if (baseType == NULL) return NULL; - if (isUniform == false) + if (variability == Varying) + // always the same, since we currently use int vectors for varying + // pointers return LLVMTypes::VoidPointerVectorType; LLVM_TYPE_CONST llvm::Type *ptype = NULL; @@ -908,12 +1119,13 @@ lCreateDIArray(llvm::DIType eltType, int count) { llvm::DIType PointerType::GetDIType(llvm::DIDescriptor scope) const { + Assert(variability != Unbound); if (baseType == NULL) return llvm::DIType(); llvm::DIType diTargetType = baseType->GetDIType(scope); int bitsSize = g->target.is32Bit ? 32 : 64; - if (isUniform) + if (variability == Uniform) return m->diBuilder->createPointerType(diTargetType, bitsSize); else { // emit them as an array of pointers @@ -954,9 +1166,9 @@ ArrayType::LLVMType(llvm::LLVMContext *ctx) const { } -bool -ArrayType::IsUniformType() const { - return child ? child->IsUniformType() : true; +Type::Variability +ArrayType::GetVariability() const { + return child ? child->GetVariability() : Uniform; } @@ -1019,6 +1231,20 @@ ArrayType::GetAsUniformType() const { } +const ArrayType * +ArrayType::GetAsUnboundVariabilityType() const { + if (child == NULL) + return NULL; + return new ArrayType(child->GetAsUnboundVariabilityType(), numElements); +} + + +const ArrayType * +ArrayType::ResolveUnboundVariability(Variability v) const { + return new ArrayType(child->ResolveUnboundVariability(v), numElements); +} + + const ArrayType * ArrayType::GetAsUnsignedType() const { if (child == NULL) @@ -1226,6 +1452,19 @@ SOAArrayType::GetAsUniformType() const { } +const SOAArrayType * +SOAArrayType::GetAsUnboundVariabilityType() const { + return new SOAArrayType(dynamic_cast(child->GetAsUnboundVariabilityType()), + numElements, soaWidth); +} + +const SOAArrayType * +SOAArrayType::ResolveUnboundVariability(Variability v) const { + const StructType *sc = dynamic_cast(child->ResolveUnboundVariability(v)); + return new SOAArrayType(sc, numElements, soaWidth); +} + + const Type * SOAArrayType::GetSOAType(int width) const { return new SOAArrayType(dynamic_cast(child->GetSOAType(width)), @@ -1342,9 +1581,9 @@ VectorType::VectorType(const AtomicType *b, int a) } -bool -VectorType::IsUniformType() const { - return base->IsUniformType(); +Type::Variability +VectorType::GetVariability() const { + return base->GetVariability(); } @@ -1396,6 +1635,18 @@ VectorType::GetAsUniformType() const { } +const VectorType * +VectorType::GetAsUnboundVariabilityType() const { + return new VectorType(base->GetAsUnboundVariabilityType(), numElements); +} + + +const VectorType * +VectorType::ResolveUnboundVariability(Variability v) const { + return new VectorType(base->ResolveUnboundVariability(v), numElements); +} + + const Type * VectorType::GetSOAType(int width) const { // FIXME: is this right?? @@ -1525,15 +1776,15 @@ VectorType::getVectorMemoryCount() const { StructType::StructType(const std::string &n, const std::vector &elts, const std::vector &en, const std::vector &ep, - bool ic, bool iu, SourcePos p) + bool ic, Variability v, SourcePos p) : name(n), elementTypes(elts), elementNames(en), elementPositions(ep), - isUniform(iu), isConst(ic), pos(p) { + variability(v), isConst(ic), pos(p) { } -bool -StructType::IsUniformType() const { - return isUniform; +Type::Variability +StructType::GetVariability() const { + return variability; } @@ -1579,7 +1830,7 @@ StructType::GetAsVaryingType() const { return this; else return new StructType(name, elementTypes, elementNames, elementPositions, - isConst, false, pos); + isConst, Varying, pos); } @@ -1589,7 +1840,34 @@ StructType::GetAsUniformType() const { return this; else return new StructType(name, elementTypes, elementNames, elementPositions, - isConst, true, pos); + isConst, Uniform, pos); +} + + +const StructType * +StructType::GetAsUnboundVariabilityType() const { + if (HasUnboundVariability()) + return this; + else + return new StructType(name, elementTypes, elementNames, elementPositions, + isConst, Unbound, pos); +} + + +const StructType * +StructType::ResolveUnboundVariability(Variability v) const { + std::vector et; + for (unsigned int i = 0; i < elementTypes.size(); ++i) + et.push_back((elementTypes[i] == NULL) ? NULL : + elementTypes[i]->ResolveUnboundVariability(v)); + + // FIXME + if (v == Varying) + v = Uniform; + + return new StructType(name, et, elementNames, elementPositions, + isConst, (variability != Unbound) ? variability : v, + pos); } @@ -1603,7 +1881,7 @@ StructType::GetSOAType(int width) const { et.push_back(t->GetSOAType(width)); } return new StructType(name, et, elementNames, elementPositions, - isConst, isUniform, pos); + isConst, variability, pos); } @@ -1613,7 +1891,7 @@ StructType::GetAsConstType() const { return this; else return new StructType(name, elementTypes, elementNames, - elementPositions, true, isUniform, pos); + elementPositions, true, variability, pos); } @@ -1623,7 +1901,7 @@ StructType::GetAsNonConstType() const { return this; else return new StructType(name, elementTypes, elementNames, elementPositions, - false, isUniform, pos); + false, variability, pos); } @@ -1631,8 +1909,12 @@ std::string StructType::GetString() const { std::string ret; if (isConst) ret += "const "; - if (isUniform) ret += "uniform "; - else ret += "varying "; + + switch (variability) { + case Uniform: ret += "uniform "; break; + case Varying: /*ret += "varying ";*/ break; + case Unbound: ret += "/*unbound*/ "; break; + } // Don't print the entire struct declaration, just print the struct's name. // @todo Do we need a separate method that prints the declaration? @@ -1655,12 +1937,16 @@ StructType::GetString() const { std::string StructType::Mangle() const { + Assert(variability != Unbound); + std::string ret; ret += "s["; if (isConst) ret += "_c_"; - if (isUniform) + if (variability == Uniform) ret += "_u_"; + else + ret += "_v_"; ret += name + std::string("]<"); for (unsigned int i = 0; i < elementTypes.size(); ++i) ret += elementTypes[i]->Mangle(); @@ -1671,16 +1957,16 @@ StructType::Mangle() const { std::string StructType::GetCDeclaration(const std::string &n) const { + if (variability != Uniform) { + Assert(m->errorCount > 0); + return ""; + } + std::string ret; if (isConst) ret += "const "; ret += std::string("struct ") + name; if (lShouldPrintName(n)) ret += std::string(" ") + n; - if (!isUniform) { - char buf[16]; - sprintf(buf, "[%d]", g->target.vectorWidth); - ret += buf; - } return ret; } @@ -1762,8 +2048,9 @@ StructType::GetElementType(int i) const { // If the struct is uniform qualified, then each member comes out with // the same type as in the original source file. If it's varying, then // all members are promoted to varying. - const Type *ret = isUniform ? elementTypes[i] : - elementTypes[i]->GetAsVaryingType(); + const Type *ret = elementTypes[i]; + if (variability == Varying) + ret = ret->GetAsVaryingType(); return isConst ? ret->GetAsConstType() : ret; } @@ -1771,11 +2058,8 @@ StructType::GetElementType(int i) const { const Type * StructType::GetElementType(const std::string &n) const { for (unsigned int i = 0; i < elementNames.size(); ++i) - if (elementNames[i] == n) { - const Type *ret = isUniform ? elementTypes[i] : - elementTypes[i]->GetAsVaryingType(); - return isConst ? ret->GetAsConstType() : ret; - } + if (elementNames[i] == n) + return GetElementType(i); return NULL; } @@ -1797,9 +2081,9 @@ ReferenceType::ReferenceType(const Type *t) } -bool -ReferenceType::IsUniformType() const { - return targetType->IsUniformType(); +Type::Variability +ReferenceType::GetVariability() const { + return targetType->GetVariability(); } @@ -1861,6 +2145,20 @@ ReferenceType::GetAsUniformType() const { } +const ReferenceType * +ReferenceType::GetAsUnboundVariabilityType() const { + if (HasUnboundVariability()) + return this; + return new ReferenceType(targetType->GetAsUnboundVariabilityType()); +} + + +const ReferenceType * +ReferenceType::ResolveUnboundVariability(Variability v) const { + return new ReferenceType(targetType->ResolveUnboundVariability(v)); +} + + const Type * ReferenceType::GetSOAType(int width) const { return new ReferenceType(targetType->GetSOAType(width)); @@ -1963,7 +2261,7 @@ FunctionType::FunctionType(const Type *r, const std::vector &a, FunctionType::FunctionType(const Type *r, const std::vector &a, - SourcePos p, const std::vector &an, + const std::vector &an, const std::vector &ad, const std::vector &ap, bool it, bool is, bool ec) @@ -1976,9 +2274,9 @@ FunctionType::FunctionType(const Type *r, const std::vector &a, } -bool -FunctionType::IsUniformType() const { - return true; +Type::Variability +FunctionType::GetVariability() const { + return Uniform; } @@ -2033,6 +2331,25 @@ FunctionType::GetAsUniformType() const { } +const Type * +FunctionType::GetAsUnboundVariabilityType() const { + FATAL("FunctionType::GetAsUnboundVariabilityType shouldn't be called"); + return NULL; +} + + +const FunctionType * +FunctionType::ResolveUnboundVariability(Variability v) const { + const Type *rt = returnType->ResolveUnboundVariability(v); + std::vector pt; + for (unsigned int i = 0; i < paramTypes.size(); ++i) + pt.push_back((paramTypes[i] == NULL) ? NULL : + paramTypes[i]->ResolveUnboundVariability(v)); + return new FunctionType(rt, pt, paramNames, paramDefaults, + paramPositions, isTask, isExported, isExternC); +} + + const Type * FunctionType::GetSOAType(int width) const { FATAL("FunctionType::GetSOAType shouldn't be called"); diff --git a/type.h b/type.h index 751e6373..5b49dead 100644 --- a/type.h +++ b/type.h @@ -78,20 +78,44 @@ public: /** Returns true if the underlying type is a float or integer type. */ bool IsNumericType() const { return IsFloatType() || IsIntType(); } + /** Types may have uniform, varying, or not-yet-determined variability; + this enumerant is used by Type implementations to record their + variability. */ + enum Variability { + Uniform, + Varying, + Unbound + }; + + /** Returns the variability of the type. */ + virtual Variability GetVariability() const = 0; + /** Returns true if the underlying type is uniform */ - virtual bool IsUniformType() const = 0; + bool IsUniformType() const { return GetVariability() == Uniform; } /** Returns true if the underlying type is varying */ - bool IsVaryingType() const { return !IsUniformType(); } + bool IsVaryingType() const { return GetVariability() == Varying; } + + /** Returns true if the underlying type's uniform/varying-ness is + unbound. */ + bool HasUnboundVariability() const { return GetVariability() == Unbound; } + + /* Returns a type wherein any elements of the original type and + contained types that have unbound variability have their variability + set to the given variability. */ + virtual const Type *ResolveUnboundVariability(Variability v) const = 0; /** Return a "uniform" instance of this type. If the type is already uniform, its "this" pointer will be returned. */ virtual const Type *GetAsUniformType() const = 0; /** Return a "varying" instance of this type. If the type is already - uniform, its "this" pointer will be returned. */ + varying, its "this" pointer will be returned. */ virtual const Type *GetAsVaryingType() const = 0; + /** Get an instance of the type with unbound variability. */ + virtual const Type *GetAsUnboundVariabilityType() const = 0; + /** If this is a signed integer type, return the unsigned version of the type. Otherwise, return the original type. */ virtual const Type *GetAsUnsignedType() const; @@ -185,7 +209,8 @@ public: */ class AtomicType : public Type { public: - bool IsUniformType() const; + Variability GetVariability() const; + bool IsBoolType() const; bool IsFloatType() const; bool IsIntType() const; @@ -195,8 +220,10 @@ public: /** For AtomicTypes, the base type is just the same as the AtomicType itself. */ const AtomicType *GetBaseType() const; - const AtomicType *GetAsVaryingType() const; const AtomicType *GetAsUniformType() const; + const AtomicType *GetAsVaryingType() const; + const AtomicType *GetAsUnboundVariabilityType() const; + const AtomicType *ResolveUnboundVariability(Variability v) const; const AtomicType *GetAsUnsignedType() const; const Type *GetSOAType(int width) const; const AtomicType *GetAsConstType() const; @@ -224,38 +251,45 @@ public: TYPE_INT64, TYPE_UINT64, TYPE_DOUBLE, + NUM_BASIC_TYPES }; const BasicType basicType; - static const AtomicType *UniformBool, *VaryingBool; - static const AtomicType *UniformInt8, *VaryingInt8; - static const AtomicType *UniformInt16, *VaryingInt16; - static const AtomicType *UniformInt32, *VaryingInt32; - static const AtomicType *UniformUInt8, *VaryingUInt8; - static const AtomicType *UniformUInt16, *VaryingUInt16; - static const AtomicType *UniformUInt32, *VaryingUInt32; - static const AtomicType *UniformFloat, *VaryingFloat; - static const AtomicType *UniformInt64, *VaryingInt64; - static const AtomicType *UniformUInt64, *VaryingUInt64; - static const AtomicType *UniformDouble, *VaryingDouble; - static const AtomicType *UniformConstBool, *VaryingConstBool; - static const AtomicType *UniformConstInt8, *VaryingConstInt8; - static const AtomicType *UniformConstInt16, *VaryingConstInt16; - static const AtomicType *UniformConstInt32, *VaryingConstInt32; - static const AtomicType *UniformConstUInt8, *VaryingConstUInt8; - static const AtomicType *UniformConstUInt16, *VaryingConstUInt16; - static const AtomicType *UniformConstUInt32, *VaryingConstUInt32; - static const AtomicType *UniformConstFloat, *VaryingConstFloat; - static const AtomicType *UniformConstInt64, *VaryingConstInt64; - static const AtomicType *UniformConstUInt64, *VaryingConstUInt64; - static const AtomicType *UniformConstDouble, *VaryingConstDouble; + static const AtomicType *UniformBool, *VaryingBool, *UnboundBool; + static const AtomicType *UniformInt8, *VaryingInt8, *UnboundInt8; + static const AtomicType *UniformInt16, *VaryingInt16, *UnboundInt16; + static const AtomicType *UniformInt32, *VaryingInt32, *UnboundInt32; + static const AtomicType *UniformUInt8, *VaryingUInt8, *UnboundUInt8; + static const AtomicType *UniformUInt16, *VaryingUInt16, *UnboundUInt16; + static const AtomicType *UniformUInt32, *VaryingUInt32, *UnboundUInt32; + static const AtomicType *UniformFloat, *VaryingFloat, *UnboundFloat; + static const AtomicType *UniformInt64, *VaryingInt64, *UnboundInt64; + static const AtomicType *UniformUInt64, *VaryingUInt64, *UnboundUInt64; + static const AtomicType *UniformDouble, *VaryingDouble, *UnboundDouble; + static const AtomicType *UniformConstBool, *VaryingConstBool, *UnboundConstBool; + static const AtomicType *UniformConstInt8, *VaryingConstInt8, *UnboundConstInt8; + static const AtomicType *UniformConstInt16, *VaryingConstInt16, *UnboundConstInt16; + static const AtomicType *UniformConstInt32, *VaryingConstInt32, *UnboundConstInt32; + static const AtomicType *UniformConstUInt8, *VaryingConstUInt8, *UnboundConstUInt8; + static const AtomicType *UniformConstUInt16, *VaryingConstUInt16, *UnboundConstUInt16; + static const AtomicType *UniformConstUInt32, *VaryingConstUInt32, *UnboundConstUInt32; + static const AtomicType *UniformConstFloat, *VaryingConstFloat, *UnboundConstFloat; + static const AtomicType *UniformConstInt64, *VaryingConstInt64, *UnboundConstInt64; + static const AtomicType *UniformConstUInt64, *VaryingConstUInt64, *UnboundConstUInt64; + static const AtomicType *UniformConstDouble, *VaryingConstDouble, *UnboundConstDouble; static const AtomicType *Void; + /** This function must be called before any of the above static const + AtomicType values is used; in practice, we do it early in + main(). */ + static void Init(); + private: - const bool isUniform; + static const AtomicType *typeTable[NUM_BASIC_TYPES][3][2]; + const Variability variability; const bool isConst; - AtomicType(BasicType basicType, bool isUniform, bool isConst); + AtomicType(BasicType basicType, Variability v, bool isConst); }; @@ -268,7 +302,8 @@ public: /** Constructor for named enumerated types */ EnumType(const char *name, SourcePos pos); - bool IsUniformType() const; + Variability GetVariability() const; + bool IsBoolType() const; bool IsFloatType() const; bool IsIntType() const; @@ -278,6 +313,8 @@ public: const EnumType *GetBaseType() const; const EnumType *GetAsVaryingType() const; const EnumType *GetAsUniformType() const; + const EnumType *GetAsUnboundVariabilityType() const; + const EnumType *ResolveUnboundVariability(Variability v) const; const Type *GetSOAType(int width) const; const EnumType *GetAsConstType() const; const EnumType *GetAsNonConstType() const; @@ -300,15 +337,17 @@ public: private: const std::string name; - bool isUniform, isConst; + Variability variability; + bool isConst; std::vector enumerators; }; + /** @brief Type implementation for pointers to other types */ class PointerType : public Type { public: - PointerType(const Type *t, bool isUniform, bool isConst); + PointerType(const Type *t, Variability v, bool isConst); /** Helper method to return a uniform pointer to the given type. */ static PointerType *GetUniform(const Type *t); @@ -318,7 +357,8 @@ public: /** Returns true if the given type is a void * type. */ static bool IsVoidPointer(const Type *t); - bool IsUniformType() const; + Variability GetVariability() const; + bool IsBoolType() const; bool IsFloatType() const; bool IsIntType() const; @@ -328,6 +368,8 @@ public: const Type *GetBaseType() const; const PointerType *GetAsVaryingType() const; const PointerType *GetAsUniformType() const; + const PointerType *GetAsUnboundVariabilityType() const; + const PointerType *ResolveUnboundVariability(Variability v) const; const Type *GetSOAType(int width) const; const PointerType *GetAsConstType() const; const PointerType *GetAsNonConstType() const; @@ -342,7 +384,8 @@ public: static PointerType *Void; private: - const bool isUniform, isConst; + const Variability variability; + const bool isConst; const Type *baseType; }; @@ -408,7 +451,8 @@ public: */ ArrayType(const Type *elementType, int numElements); - bool IsUniformType() const; + Variability GetVariability() const; + bool IsBoolType() const; bool IsFloatType() const; bool IsIntType() const; @@ -418,6 +462,9 @@ public: const Type *GetBaseType() const; const ArrayType *GetAsVaryingType() const; const ArrayType *GetAsUniformType() const; + const ArrayType *GetAsUnboundVariabilityType() const; + const ArrayType *ResolveUnboundVariability(Variability v) const; + const ArrayType *GetAsUnsignedType() const; const Type *GetSOAType(int width) const; const ArrayType *GetAsConstType() const; @@ -495,6 +542,9 @@ public: const SOAArrayType *GetAsVaryingType() const; const SOAArrayType *GetAsUniformType() const; + const SOAArrayType *GetAsUnboundVariabilityType() const; + const SOAArrayType *ResolveUnboundVariability(Variability v) const; + const Type *GetSOAType(int width) const; const SOAArrayType *GetAsConstType() const; const SOAArrayType *GetAsNonConstType() const; @@ -536,7 +586,8 @@ class VectorType : public SequentialType { public: VectorType(const AtomicType *base, int size); - bool IsUniformType() const; + Variability GetVariability() const; + bool IsBoolType() const; bool IsFloatType() const; bool IsIntType() const; @@ -546,6 +597,9 @@ public: const Type *GetBaseType() const; const VectorType *GetAsVaryingType() const; const VectorType *GetAsUniformType() const; + const VectorType *GetAsUnboundVariabilityType() const; + const VectorType *ResolveUnboundVariability(Variability v) const; + const Type *GetSOAType(int width) const; const VectorType *GetAsConstType() const; const VectorType *GetAsNonConstType() const; @@ -580,9 +634,10 @@ public: StructType(const std::string &name, const std::vector &elts, const std::vector &eltNames, const std::vector &eltPositions, bool isConst, - bool isUniform, SourcePos pos); + Variability variability, SourcePos pos); + + Variability GetVariability() const; - bool IsUniformType() const; bool IsBoolType() const; bool IsFloatType() const; bool IsIntType() const; @@ -592,6 +647,9 @@ public: const Type *GetBaseType() const; const StructType *GetAsVaryingType() const; const StructType *GetAsUniformType() const; + const StructType *GetAsUnboundVariabilityType() const; + const StructType *ResolveUnboundVariability(Variability v) const; + const Type *GetSOAType(int width) const; const StructType *GetAsConstType() const; const StructType *GetAsNonConstType() const; @@ -641,7 +699,7 @@ private: /** Source file position at which each structure element declaration appeared. */ const std::vector elementPositions; - const bool isUniform; + const Variability variability; const bool isConst; const SourcePos pos; }; @@ -653,7 +711,8 @@ class ReferenceType : public Type { public: ReferenceType(const Type *targetType); - bool IsUniformType() const; + Variability GetVariability() const; + bool IsBoolType() const; bool IsFloatType() const; bool IsIntType() const; @@ -664,6 +723,9 @@ public: const Type *GetReferenceTarget() const; const ReferenceType *GetAsVaryingType() const; const ReferenceType *GetAsUniformType() const; + const ReferenceType *GetAsUnboundVariabilityType() const; + const ReferenceType *ResolveUnboundVariability(Variability v) const; + const Type *GetSOAType(int width) const; const ReferenceType *GetAsConstType() const; const ReferenceType *GetAsNonConstType() const; @@ -696,13 +758,14 @@ public: FunctionType(const Type *returnType, const std::vector &argTypes, SourcePos pos); FunctionType(const Type *returnType, - const std::vector &argTypes, SourcePos pos, + const std::vector &argTypes, const std::vector &argNames, const std::vector &argDefaults, const std::vector &argPos, bool isTask, bool isExported, bool isExternC); - bool IsUniformType() const; + Variability GetVariability() const; + bool IsBoolType() const; bool IsFloatType() const; bool IsIntType() const; @@ -712,6 +775,9 @@ public: const Type *GetBaseType() const; const Type *GetAsVaryingType() const; const Type *GetAsUniformType() const; + const Type *GetAsUnboundVariabilityType() const; + const FunctionType *ResolveUnboundVariability(Variability v) const; + const Type *GetSOAType(int width) const; const Type *GetAsConstType() const; const Type *GetAsNonConstType() const; @@ -752,6 +818,7 @@ public: private: const Type * const returnType; + // The following four vectors should all have the same length (which is // in turn the length returned by GetNumParameters()). const std::vector paramTypes; From 9b68b9087a2653160e348acd7f3f5d71e1d3fa95 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Fri, 6 Jan 2012 13:28:06 -0800 Subject: [PATCH 07/60] Fix crash with anonymous function parameters in function definitions. Issue #135. --- decl.cpp | 54 +++++++++++++++++++++++++++++++----------------------- decl.h | 2 ++ 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/decl.cpp b/decl.cpp index 41ff8bac..ae7e78a5 100644 --- a/decl.cpp +++ b/decl.cpp @@ -261,10 +261,7 @@ Declarator::GetFunctionInfo(DeclSpecs *ds, std::vector *funArgs) { Assert(d != NULL); for (unsigned int i = 0; i < d->functionParams.size(); ++i) { - Declaration *pdecl = d->functionParams[i]; - Assert(pdecl->declarators.size() == 1); - - Symbol *sym = pdecl->declarators[0]->GetSymbol(); + Symbol *sym = d->GetSymbolForFunctionParameter(i); sym->type = sym->type->ResolveUnboundVariability(Type::Varying); funArgs->push_back(sym); } @@ -353,25 +350,7 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { for (unsigned int i = 0; i < functionParams.size(); ++i) { Declaration *d = functionParams[i]; - char buf[32]; - Symbol *sym; - if (d->declarators.size() == 0) { - // function declaration like foo(float), w/o a name for - // the parameter - sprintf(buf, "__anon_parameter_%d", i); - sym = new Symbol(buf, pos); - sym->type = d->declSpecs->GetBaseType(pos); - } - else { - sym = d->declarators[0]->GetSymbol(); - if (sym == NULL) { - // Handle more complex anonymous declarations like - // float (float **). - sprintf(buf, "__anon_parameter_%d", i); - sym = new Symbol(buf, d->declarators[0]->pos); - sym->type = d->declarators[0]->GetType(d->declSpecs); - } - } + Symbol *sym = GetSymbolForFunctionParameter(i); if (d->declSpecs->storageClass != SC_NONE) Error(sym->pos, "Storage class \"%s\" is illegal in " @@ -499,6 +478,35 @@ Declarator::GetType(DeclSpecs *ds) const { } +Symbol * +Declarator::GetSymbolForFunctionParameter(int paramNum) const { + Assert(paramNum < (int)functionParams.size()); + Declaration *d = functionParams[paramNum]; + + char buf[32]; + Symbol *sym; + if (d->declarators.size() == 0) { + // function declaration like foo(float), w/o a name for + // the parameter + sprintf(buf, "__anon_parameter_%d", paramNum); + sym = new Symbol(buf, pos); + sym->type = d->declSpecs->GetBaseType(pos); + } + else { + Assert(d->declarators.size() == 1); + sym = d->declarators[0]->GetSymbol(); + if (sym == NULL) { + // Handle more complex anonymous declarations like + // float (float **). + sprintf(buf, "__anon_parameter_%d", paramNum); + sym = new Symbol(buf, d->declarators[0]->pos); + sym->type = d->declarators[0]->GetType(d->declSpecs); + } + } + return sym; +} + + /////////////////////////////////////////////////////////////////////////// // Declaration diff --git a/decl.h b/decl.h index ebeee9a4..272ccea2 100644 --- a/decl.h +++ b/decl.h @@ -153,6 +153,8 @@ public: declarator and symbols for its arguments in *args. */ Symbol *GetFunctionInfo(DeclSpecs *ds, std::vector *args); + Symbol *GetSymbolForFunctionParameter(int paramNum) const; + /** Returns the symbol associated with the declarator. */ Symbol *GetSymbol() const; From 4f97262cf22495f842c56b02de748b0b2de399ee Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Fri, 6 Jan 2012 13:50:10 -0800 Subject: [PATCH 08/60] Support function declarations in the definitions of other functions. As part of this, function declarations are no longer scoped (this is permitted by the C standard, as it turns out.) So code like: void foo() { void bar(); } void bat() { bar(); } Compiles correctly; the declaration of bar() in foo() is still available in the definition of bar(). Fixes issue #129. --- decl.cpp | 30 +++++++++++++++++------ decl.h | 4 ++++ parse.yy | 1 + sym.cpp | 73 ++++++++++++++++++++++---------------------------------- sym.h | 26 ++++++++++---------- 5 files changed, 68 insertions(+), 66 deletions(-) diff --git a/decl.cpp b/decl.cpp index ae7e78a5..5ec58462 100644 --- a/decl.cpp +++ b/decl.cpp @@ -535,8 +535,6 @@ Declaration::GetVariableDeclarations() const { std::vector vars; for (unsigned int i = 0; i < declarators.size(); ++i) { - if (declarators[i] == NULL) - continue; Declarator *decl = declarators[i]; if (decl == NULL) // Ignore earlier errors @@ -545,11 +543,7 @@ Declaration::GetVariableDeclarations() const { Symbol *sym = decl->GetSymbol(); sym->type = sym->type->ResolveUnboundVariability(Type::Varying); - if (dynamic_cast(sym->type) != NULL) { - // function declaration - m->symbolTable->AddFunction(sym); - } - else { + if (dynamic_cast(sym->type) == NULL) { m->symbolTable->AddVariable(sym); vars.push_back(VariableDeclaration(sym, decl->initExpr)); } @@ -558,6 +552,28 @@ Declaration::GetVariableDeclarations() const { } +void +Declaration::DeclareFunctions() { + Assert(declSpecs->storageClass != SC_TYPEDEF); + + for (unsigned int i = 0; i < declarators.size(); ++i) { + Declarator *decl = declarators[i]; + if (decl == NULL) + // Ignore earlier errors + continue; + + Symbol *sym = decl->GetSymbol(); + sym->type = sym->type->ResolveUnboundVariability(Type::Varying); + + if (dynamic_cast(sym->type) == NULL) + continue; + + bool isInline = (declSpecs->typeQualifiers & TYPEQUAL_INLINE); + m->AddFunctionDeclaration(sym, isInline); + } +} + + void Declaration::Print(int indent) const { printf("%*cDeclaration: specs [", indent, ' '); diff --git a/decl.h b/decl.h index 272ccea2..2d7e662b 100644 --- a/decl.h +++ b/decl.h @@ -210,6 +210,10 @@ public: Declarator representation.) */ std::vector GetVariableDeclarations() const; + /** For any function declarations in the Declaration, add the + declaration to the module. */ + void DeclareFunctions(); + DeclSpecs *declSpecs; std::vector declarators; }; diff --git a/parse.yy b/parse.yy index 97cc6eff..07575948 100644 --- a/parse.yy +++ b/parse.yy @@ -494,6 +494,7 @@ declaration_statement $$ = NULL; } else { + $1->DeclareFunctions(); std::vector vars = $1->GetVariableDeclarations(); $$ = new DeclStmt(vars, @1); } diff --git a/sym.cpp b/sym.cpp index b33e8cbe..0647a5b4 100644 --- a/sym.cpp +++ b/sym.cpp @@ -72,8 +72,7 @@ SymbolTable::SymbolTable() { SymbolTable::~SymbolTable() { // Otherwise we have mismatched push/pop scopes - Assert(variables.size() == 1 && functions.size() == 1 && - types.size() == 1); + Assert(variables.size() == 1 && types.size() == 1); PopScope(); } @@ -81,7 +80,6 @@ SymbolTable::~SymbolTable() { void SymbolTable::PushScope() { variables.push_back(new SymbolMapType); - functions.push_back(new FunctionMapType); types.push_back(new TypeMapType); } @@ -92,10 +90,6 @@ SymbolTable::PopScope() { delete variables.back(); variables.pop_back(); - Assert(functions.size() > 1); - delete functions.back(); - functions.pop_back(); - Assert(types.size() > 1); delete types.back(); types.pop_back(); @@ -160,7 +154,7 @@ SymbolTable::AddFunction(Symbol *symbol) { // the symbol table return false; - std::vector &funOverloads = (*functions.back())[symbol->name]; + std::vector &funOverloads = functions[symbol->name]; funOverloads.push_back(symbol); return true; } @@ -168,17 +162,14 @@ SymbolTable::AddFunction(Symbol *symbol) { bool SymbolTable::LookupFunction(const char *name, std::vector *matches) { - for (int i = (int)functions.size() - 1; i >= 0; --i) { - FunctionMapType &fm = *(functions[i]); - FunctionMapType::iterator iter = fm.find(name); - if (iter != fm.end()) { - if (matches == NULL) - return true; - else { - const std::vector &funcs = iter->second; - for (int j = 0; j < (int)funcs.size(); ++j) - matches->push_back(funcs[j]); - } + FunctionMapType::iterator iter = functions.find(name); + if (iter != functions.end()) { + if (matches == NULL) + return true; + else { + const std::vector &funcs = iter->second; + for (int j = 0; j < (int)funcs.size(); ++j) + matches->push_back(funcs[j]); } } return matches ? (matches->size() > 0) : false; @@ -187,15 +178,12 @@ SymbolTable::LookupFunction(const char *name, std::vector *matches) { Symbol * SymbolTable::LookupFunction(const char *name, const FunctionType *type) { - for (int i = (int)functions.size() - 1; i >= 0; --i) { - FunctionMapType &fm = *(functions[i]); - FunctionMapType::iterator iter = fm.find(name); - if (iter != fm.end()) { - std::vector funcs = iter->second; - for (int j = 0; j < (int)funcs.size(); ++j) { - if (Type::Equal(funcs[j]->type, type)) - return funcs[j]; - } + FunctionMapType::iterator iter = functions.find(name); + if (iter != functions.end()) { + std::vector funcs = iter->second; + for (int j = 0; j < (int)funcs.size(); ++j) { + if (Type::Equal(funcs[j]->type, type)) + return funcs[j]; } } return NULL; @@ -261,14 +249,11 @@ SymbolTable::ClosestVariableOrFunctionMatch(const char *str) const { } } - for (int i = 0; i < (int)functions.size(); ++i) { - const FunctionMapType &fm = *(functions[i]); - FunctionMapType::const_iterator iter; - for (iter = fm.begin(); iter != fm.end(); ++iter) { - int dist = StringEditDistance(str, iter->first, maxDelta+1); - if (dist <= maxDelta) - matches[dist].push_back(iter->first); - } + FunctionMapType::const_iterator iter; + for (iter = functions.begin(); iter != functions.end(); ++iter) { + int dist = StringEditDistance(str, iter->first, maxDelta+1); + if (dist <= maxDelta) + matches[dist].push_back(iter->first); } // Now, return the first entry of matches[] that is non-empty, if any. @@ -346,15 +331,13 @@ SymbolTable::Print() { } fprintf(stderr, "Functions:\n----------------\n"); - for (int i = 0; i < (int)functions.size(); ++i) { - FunctionMapType::iterator fiter = functions[i]->begin(); - while (fiter != functions[i]->end()) { - fprintf(stderr, "%s\n", fiter->first.c_str()); - std::vector &syms = fiter->second; - for (unsigned int j = 0; j < syms.size(); ++j) - fprintf(stderr, " %s\n", syms[j]->type->GetString().c_str()); - ++fiter; - } + FunctionMapType::iterator fiter = functions.begin(); + while (fiter != functions.end()) { + fprintf(stderr, "%s\n", fiter->first.c_str()); + std::vector &syms = fiter->second; + for (unsigned int j = 0; j < syms.size(); ++j) + fprintf(stderr, " %s\n", syms[j]->type->GetString().c_str()); + ++fiter; } depth = 0; diff --git a/sym.h b/sym.h index 97f5efd7..aff0553c 100644 --- a/sym.h +++ b/sym.h @@ -257,12 +257,13 @@ private: typedef std::map SymbolMapType; std::vector variables; - /** Function declarations are also scoped., A STL \c vector is used to - store the function symbols for a given name since, due to function - overloading, a name can have multiple function symbols associated - with it. */ + /** Function declarations are *not* scoped. (C99, for example, allows + an implementation to maintain function declarations in a single + namespace.) A STL \c vector is used to store the function symbols + for a given name since, due to function overloading, a name can + have multiple function symbols associated with it. */ typedef std::map > FunctionMapType; - std::vector functions; + FunctionMapType functions; /** Type definitions can also be scoped. A new \c TypeMapType is added to the back of the \c types \c vector each time a new scope @@ -278,15 +279,12 @@ SymbolTable::GetMatchingFunctions(Predicate pred, std::vector *matches) const { // Iterate through all function symbols and apply the given predicate. // If it returns true, add the Symbol * to the provided vector. - for (unsigned int i = 0; i < functions.size(); ++i) { - FunctionMapType &fm = *(functions[i]); - FunctionMapType::const_iterator iter; - for (iter = fm.begin(); iter != fm.end(); ++iter) { - const std::vector &syms = iter->second; - for (unsigned int j = 0; j < syms.size(); ++j) { - if (pred(syms[j])) - matches->push_back(syms[j]); - } + FunctionMapType::const_iterator iter; + for (iter = functions.begin(); iter != functions.end(); ++iter) { + const std::vector &syms = iter->second; + for (unsigned int j = 0; j < syms.size(); ++j) { + if (pred(syms[j])) + matches->push_back(syms[j]); } } } From 11033e108e557a0198e0cf4117eb72e1f431c63e Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Fri, 6 Jan 2012 14:21:03 -0800 Subject: [PATCH 09/60] Fix bug that prohibited assignments with pointer expressions on the LHS Previously, code like "*(ptr+1) = foo" would claim that the LHS was invalid for an assignment expression. Issue #138. --- expr.cpp | 25 +++++++++++-------------- tests/ptr-assign-lhs-math-1.ispc | 16 ++++++++++++++++ tests/ptr-assign-lhs-math-2.ispc | 15 +++++++++++++++ tests_errors/lvalue-1.ispc | 2 +- tests_errors/lvalue-2.ispc | 2 +- tests_errors/lvalue-3.ispc | 2 +- 6 files changed, 45 insertions(+), 17 deletions(-) create mode 100644 tests/ptr-assign-lhs-math-1.ispc create mode 100644 tests/ptr-assign-lhs-math-2.ispc diff --git a/expr.cpp b/expr.cpp index 2fd3145f..1bcfa70d 100644 --- a/expr.cpp +++ b/expr.cpp @@ -639,6 +639,9 @@ lLLVMConstantValue(const Type *type, llvm::LLVMContext *ctx, double value) { static llvm::Value * lMaskForSymbol(Symbol *baseSym, FunctionEmitContext *ctx) { + if (baseSym == NULL) + return ctx->GetFullMask(); + if (dynamic_cast(baseSym->type) != NULL || dynamic_cast(baseSym->type) != NULL) // FIXME: for pointers, we really only want to do this for @@ -659,10 +662,11 @@ lMaskForSymbol(Symbol *baseSym, FunctionEmitContext *ctx) { static void lStoreAssignResult(llvm::Value *value, llvm::Value *ptr, const Type *ptrType, FunctionEmitContext *ctx, Symbol *baseSym) { - Assert(baseSym != NULL && + Assert(baseSym == NULL || baseSym->varyingCFDepth <= ctx->VaryingCFDepth()); if (!g->opt.disableMaskedStoreToStore && !g->opt.disableMaskAllOnOptimizations && + baseSym != NULL && baseSym->varyingCFDepth == ctx->VaryingCFDepth() && baseSym->storageClass != SC_STATIC && dynamic_cast(baseSym->type) == NULL && @@ -2017,14 +2021,13 @@ AssignExpr::GetValue(FunctionEmitContext *ctx) const { ctx->SetDebugPos(pos); Symbol *baseSym = lvalue->GetBaseSymbol(); - // Should be caught during type-checking... - assert(baseSym != NULL); switch (op) { case Assign: { llvm::Value *lv = lvalue->GetLValue(ctx); if (lv == NULL) { - Assert(m->errorCount > 0); + Error(lvalue->pos, "Left hand side of assignment expression can't " + "be assigned to."); return NULL; } const Type *lvalueType = lvalue->GetLValueType(); @@ -2147,13 +2150,13 @@ AssignExpr::TypeCheck() { } } - if (lvalue->GetBaseSymbol() == NULL) { - Error(lvalue->pos, "Left hand side of assignment statement can't be " - "assigned to."); + const Type *lhsType = lvalue->GetType(); + if (lhsType->IsConstType()) { + Error(lvalue->pos, "Can't assign to type \"%s\" on left-hand side of " + "expression.", lhsType->GetString().c_str()); return NULL; } - const Type *lhsType = lvalue->GetType(); if (dynamic_cast(lhsType) != NULL) { if (op == AddAssign || op == SubAssign) { if (PointerType::IsVoidPointer(lhsType)) { @@ -2187,12 +2190,6 @@ AssignExpr::TypeCheck() { if (rvalue == NULL) return NULL; - if (lhsType->IsConstType()) { - Error(pos, "Can't assign to type \"%s\" on left-hand side of " - "expression.", lhsType->GetString().c_str()); - return NULL; - } - // Make sure we're not assigning to a struct that has a constant member const StructType *st = dynamic_cast(lhsType); if (st != NULL && lCheckForConstStructMember(pos, st, st)) diff --git a/tests/ptr-assign-lhs-math-1.ispc b/tests/ptr-assign-lhs-math-1.ispc new file mode 100644 index 00000000..2b34cbae --- /dev/null +++ b/tests/ptr-assign-lhs-math-1.ispc @@ -0,0 +1,16 @@ + +export uniform int width() { return programCount; } + +export void f_f(uniform float RET[], uniform float aFOO[]) { + uniform float a[programCount]; + a[programIndex] = aFOO[programIndex]; + + uniform float * uniform ptr = a; + *(ptr+1) = 0; + RET[programIndex] = a[programIndex]; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 1+programIndex; + RET[1] = 0; +} diff --git a/tests/ptr-assign-lhs-math-2.ispc b/tests/ptr-assign-lhs-math-2.ispc new file mode 100644 index 00000000..0e28cca2 --- /dev/null +++ b/tests/ptr-assign-lhs-math-2.ispc @@ -0,0 +1,15 @@ + +export uniform int width() { return programCount; } + +export void f_f(uniform float RET[], uniform float aFOO[]) { + uniform float a[programCount]; + a[programIndex] = aFOO[programIndex]; + + uniform float * varying ptr = a; + *(ptr+programIndex) = 0; + RET[programIndex] = a[programIndex]; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 0; +} diff --git a/tests_errors/lvalue-1.ispc b/tests_errors/lvalue-1.ispc index ada9abf3..8140a2c8 100644 --- a/tests_errors/lvalue-1.ispc +++ b/tests_errors/lvalue-1.ispc @@ -1,4 +1,4 @@ -// Left hand side of assignment statement can't be assigned to +// Left hand side of assignment expression can't be assigned to int foo() {return 2;} diff --git a/tests_errors/lvalue-2.ispc b/tests_errors/lvalue-2.ispc index ef067008..ae2e3edd 100644 --- a/tests_errors/lvalue-2.ispc +++ b/tests_errors/lvalue-2.ispc @@ -1,4 +1,4 @@ -// Left hand side of assignment statement can't be assigned to +// Can't assign to type "const uniform int32" on left-hand side of expression int bar(){ 4 = 0; diff --git a/tests_errors/lvalue-3.ispc b/tests_errors/lvalue-3.ispc index f4fb830d..eb856bf0 100644 --- a/tests_errors/lvalue-3.ispc +++ b/tests_errors/lvalue-3.ispc @@ -1,4 +1,4 @@ -// Left hand side of assignment statement can't be assigned to +// Can't assign to type "const uniform int32" on left-hand side of expression int bar(){ int x; From 8da9be1a0925258ef3807fcb856d3010777191e1 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Fri, 6 Jan 2012 14:47:47 -0800 Subject: [PATCH 10/60] Add support for 'k', 'M', and 'G' suffixes to integer constants. (Denoting units of 1024, 1024*1024, and 1024*1024*1024, respectively.) Issue #128. --- docs/ispc.txt | 12 +++++++++ lex.ll | 52 +++++++++++++++++++++++-------------- tests/kilo-mega-giga-1.ispc | 13 ++++++++++ tests/kilo-mega-giga-2.ispc | 12 +++++++++ tests/kilo-mega-giga-3.ispc | 14 ++++++++++ 5 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 tests/kilo-mega-giga-1.ispc create mode 100644 tests/kilo-mega-giga-2.ispc create mode 100644 tests/kilo-mega-giga-3.ispc diff --git a/docs/ispc.txt b/docs/ispc.txt index 5d9ea2b0..32e5017c 100644 --- a/docs/ispc.txt +++ b/docs/ispc.txt @@ -1247,6 +1247,18 @@ Here are three ways of specifying the integer value "15": int fifteen_hex = 0xf; int fifteen_binary = 0b1111; +A number of suffixes can be provided with integer numeric constants. +First, "u" denotes that the constant is unsigned, and "ll" denotes a 64-bit +integer constant (while "l" denotes a 32-bit integer constant). It is also +possible to denote units of 1024, 1024*1024, or 1024*1024*1024 with the +SI-inspired suffixes "k", "M", and "G" respectively: + +:: + + int two_kb = 2k; // 2048 + int two_megs = 2M; // 2 * 1024 * 1024 + int one_gig = 1G; // 1024 * 1024 * 1024 + Floating-point constants can be specified in one of three ways. First, they may be a sequence of zero or more digits from 0 to 9, followed by a period, followed by zero or more digits from 0 to 9. (There must be at diff --git a/lex.ll b/lex.ll index f4161c94..39194767 100644 --- a/lex.ll +++ b/lex.ll @@ -42,7 +42,7 @@ #include #include -static uint64_t lParseBinary(const char *ptr, SourcePos pos); +static uint64_t lParseBinary(const char *ptr, SourcePos pos, char **endPtr); static void lCComment(SourcePos *); static void lCppComment(SourcePos *); static void lHandleCppHash(SourcePos *); @@ -67,7 +67,7 @@ inline int isatty(int) { return 0; } %option nounistd WHITESPACE [ \t\r]+ -INT_NUMBER (([0-9]+)|(0x[0-9a-fA-F]+)|(0b[01]+)) +INT_NUMBER (([0-9]+)|(0x[0-9a-fA-F]+)|(0b[01]+))[kMG]? FLOAT_NUMBER (([0-9]+|(([0-9]+\.[0-9]*[fF]?)|(\.[0-9]+)))([eE][-+]?[0-9]+)?[fF]?) HEX_FLOAT_NUMBER (0x[01](\.[0-9a-fA-F]*)?p[-+]?[0-9]+[fF]?) @@ -151,11 +151,10 @@ L?\"(\\.|[^\\"])*\" { lStringConst(yylval, yylloc); return TOKEN_STRING_LITERAL; {INT_NUMBER}+(u|U|l|L)*? { int ls = 0, us = 0; + char *endPtr = NULL; if (yytext[0] == '0' && yytext[1] == 'b') - yylval->intVal = lParseBinary(yytext+2, *yylloc); + yylval->intVal = lParseBinary(yytext+2, *yylloc, &endPtr); else { - char *endPtr = NULL; - #ifdef ISPC_IS_WINDOWS yylval->intVal = _strtoi64(yytext, &endPtr, 0); #else @@ -163,18 +162,33 @@ L?\"(\\.|[^\\"])*\" { lStringConst(yylval, yylloc); return TOKEN_STRING_LITERAL; // fit into 64 bits... yylval->intVal = strtoull(yytext, &endPtr, 0); #endif - for (; *endPtr; endPtr++) { - if (*endPtr == 'l' || *endPtr == 'L') - ls++; - else if (*endPtr == 'u' || *endPtr == 'U') - us++; - } - if (ls >= 2) - return us ? TOKEN_UINT64_CONSTANT : TOKEN_INT64_CONSTANT; - else if (ls == 1) - return us ? TOKEN_UINT32_CONSTANT : TOKEN_INT32_CONSTANT; } + bool kilo = false, mega = false, giga = false; + for (; *endPtr; endPtr++) { + if (*endPtr == 'k') + kilo = true; + else if (*endPtr == 'M') + mega = true; + else if (*endPtr == 'G') + giga = true; + else if (*endPtr == 'l' || *endPtr == 'L') + ls++; + else if (*endPtr == 'u' || *endPtr == 'U') + us++; + } + if (kilo) + yylval->intVal *= 1024; + if (mega) + yylval->intVal *= 1024*1024; + if (giga) + yylval->intVal *= 1024*1024*1024; + + if (ls >= 2) + return us ? TOKEN_UINT64_CONSTANT : TOKEN_INT64_CONSTANT; + else if (ls == 1) + return us ? TOKEN_UINT32_CONSTANT : TOKEN_INT32_CONSTANT; + // See if we can fit this into a 32-bit integer... if ((yylval->intVal & 0xffffffff) == yylval->intVal) return us ? TOKEN_UINT32_CONSTANT : TOKEN_INT32_CONSTANT; @@ -268,14 +282,11 @@ L?\"(\\.|[^\\"])*\" { lStringConst(yylval, yylloc); return TOKEN_STRING_LITERAL; /** Return the integer version of a binary constant from a string. */ static uint64_t -lParseBinary(const char *ptr, SourcePos pos) { +lParseBinary(const char *ptr, SourcePos pos, char **endPtr) { uint64_t val = 0; bool warned = false; - while (*ptr != '\0') { - /* if this hits, the regexp for 0b... constants is broken */ - Assert(*ptr == '0' || *ptr == '1'); - + while (*ptr == '0' || *ptr == '1') { if ((val & (((int64_t)1)<<63)) && warned == false) { // We're about to shift out a set bit Warning(pos, "Can't represent binary constant with a 64-bit integer type"); @@ -285,6 +296,7 @@ lParseBinary(const char *ptr, SourcePos pos) { val = (val << 1) | (*ptr == '0' ? 0 : 1); ++ptr; } + *endPtr = (char *)ptr; return val; } diff --git a/tests/kilo-mega-giga-1.ispc b/tests/kilo-mega-giga-1.ispc new file mode 100644 index 00000000..9ae8f87e --- /dev/null +++ b/tests/kilo-mega-giga-1.ispc @@ -0,0 +1,13 @@ + +export uniform int width() { return programCount; } + + +export void f_f(uniform float RET[], uniform float aFOO[]) { + float a = aFOO[programIndex]; + a *= 1k; + RET[programIndex] = a; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 1024*(programIndex+1); +} diff --git a/tests/kilo-mega-giga-2.ispc b/tests/kilo-mega-giga-2.ispc new file mode 100644 index 00000000..77e201ef --- /dev/null +++ b/tests/kilo-mega-giga-2.ispc @@ -0,0 +1,12 @@ + +export uniform int width() { return programCount; } + + +export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) { + int a = b + 2M; + RET[programIndex] = a; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 2*1024*1024 + 5; +} diff --git a/tests/kilo-mega-giga-3.ispc b/tests/kilo-mega-giga-3.ispc new file mode 100644 index 00000000..aa80e8be --- /dev/null +++ b/tests/kilo-mega-giga-3.ispc @@ -0,0 +1,14 @@ + +export uniform int width() { return programCount; } + + +export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) { + unsigned int32 a = 3G; + a -= 2G; + a -= 1024M; + RET[programIndex] = a; +} + +export void result(uniform float RET[]) { + RET[programIndex] = 0; +} From 5b4dbc81674ec40046e43e30959c32269ba8dd68 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Sun, 8 Jan 2012 10:02:43 -0800 Subject: [PATCH 11/60] Fix build of aobench_instrumented example on OSX/Linux --- examples/aobench_instrumented/Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/aobench_instrumented/Makefile b/examples/aobench_instrumented/Makefile index bfcdfc48..9921cf3e 100644 --- a/examples/aobench_instrumented/Makefile +++ b/examples/aobench_instrumented/Makefile @@ -14,13 +14,13 @@ dirs: clean: /bin/rm -rf objs *~ ao -ao: dirs objs/ao.o objs/instrument.o objs/ao_ispc.o - $(CXX) $(CXXFLAGS) -o $@ objs/ao.o objs/ao_ispc.o objs/instrument.o -lm -lpthread +ao: objs/ao.o objs/instrument.o objs/ao_ispc.o ../tasksys.cpp + $(CXX) $(CXXFLAGS) -o $@ $^ -lm -lpthread -objs/%.o: %.cpp +objs/%.o: %.cpp dirs $(CXX) $< $(CXXFLAGS) -c -o $@ objs/ao.o: objs/ao_ispc.h -objs/%_ispc.h objs/%_ispc.o: %.ispc - $(ISPC) $(ISPCFLAGS) $< -o objs/$*_ispc.o -h objs/$*_ispc.h +objs/%_ispc.h objs/%_ispc.o: %.ispc dirs + $(ISPC) $(ISPCFLAGS) $< -o objs/$*_ispc.o -h objs/$*_instrumented_ispc.h From ff6971fb15f1f4a9ef421d1db685cf2256160e02 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Sun, 8 Jan 2012 13:30:45 -0800 Subject: [PATCH 12/60] Use Assert() rather than assert() --- ast.cpp | 2 +- llvmutil.cpp | 2 +- opt.cpp | 4 ++-- stmt.cpp | 2 +- test_static.cpp | 1 - 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ast.cpp b/ast.cpp index 40503219..f260c0b5 100644 --- a/ast.cpp +++ b/ast.cpp @@ -155,7 +155,7 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc, else { /////////////////////////////////////////////////////////////////////////// // Handle expressions - assert(dynamic_cast(node) != NULL); + Assert(dynamic_cast(node) != NULL); UnaryExpr *ue; BinaryExpr *be; AssignExpr *ae; diff --git a/llvmutil.cpp b/llvmutil.cpp index 4a50e337..804b13e0 100644 --- a/llvmutil.cpp +++ b/llvmutil.cpp @@ -109,7 +109,7 @@ InitLLVMUtil(llvm::LLVMContext *ctx, Target target) { LLVMTypes::MaskType = LLVMTypes::BoolVectorType = llvm::VectorType::get(llvm::Type::getInt1Ty(*ctx), target.vectorWidth); else { - assert(target.maskBitCount == 32); + Assert(target.maskBitCount == 32); LLVMTypes::MaskType = LLVMTypes::BoolVectorType = llvm::VectorType::get(llvm::Type::getInt32Ty(*ctx), target.vectorWidth); } diff --git a/opt.cpp b/opt.cpp index b30efd10..3510fbb2 100644 --- a/opt.cpp +++ b/opt.cpp @@ -550,7 +550,7 @@ lGetMask(llvm::Value *factor) { /* FIXME: This will break if we ever do 32-wide compilation, in which case it don't be possible to distinguish between -1 for "don't know" and "known and all bits on". */ - assert(g->target.vectorWidth < 32); + Assert(g->target.vectorWidth < 32); llvm::ConstantVector *cv = llvm::dyn_cast(factor); if (cv) { @@ -2266,7 +2266,7 @@ GSImprovementsPass::runOnBasicBlock(llvm::BasicBlock &bb) { llvm::ConstantInt *offsetScaleInt = llvm::dyn_cast(offsetScale); - assert(offsetScaleInt != NULL); + Assert(offsetScaleInt != NULL); if (offsets->getType() == LLVMTypes::Int64VectorType) // offsetScale is an i32, so sext it so that if we use it in a diff --git a/stmt.cpp b/stmt.cpp index 5a803366..043f6fa2 100644 --- a/stmt.cpp +++ b/stmt.cpp @@ -1989,7 +1989,7 @@ LabeledStmt::LabeledStmt(const char *n, Stmt *s, SourcePos p) void LabeledStmt::EmitCode(FunctionEmitContext *ctx) const { llvm::BasicBlock *bblock = ctx->GetLabeledBasicBlock(name); - assert(bblock != NULL); + Assert(bblock != NULL); // End the current basic block with a jump to our basic block and then // set things up for emission to continue there. Note that the current diff --git a/test_static.cpp b/test_static.cpp index 48a0021f..a8ec4a79 100644 --- a/test_static.cpp +++ b/test_static.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #include #ifdef ISPC_IS_LINUX #include From 0ed11a7832b1b462a03cd83a91c0edce42bd7408 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Sun, 8 Jan 2012 14:01:14 -0800 Subject: [PATCH 13/60] Compute SizeOf() and OffsetOf() at compile time in more cases with the generic target. Really, we only have to be careful about the case where there is a vector of bools (i.e. a mask) involved, since the size of that isn't known at compile-time. (Currently, at least.) --- ispc.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/ispc.cpp b/ispc.cpp index 750e7f9e..85f81b3c 100644 --- a/ispc.cpp +++ b/ispc.cpp @@ -358,10 +358,46 @@ Target::GetISAString() const { } +static bool +lGenericTypeLayoutIndeterminate(LLVM_TYPE_CONST llvm::Type *type) { + if (type->isPrimitiveType() || type->isIntegerTy()) + return false; + + if (type == LLVMTypes::BoolVectorType || + type == LLVMTypes::MaskType || + type == LLVMTypes::Int1VectorType) + return true; + + LLVM_TYPE_CONST llvm::ArrayType *at = + llvm::dyn_cast(type); + if (at != NULL) + return lGenericTypeLayoutIndeterminate(at->getElementType()); + + LLVM_TYPE_CONST llvm::PointerType *pt = + llvm::dyn_cast(type); + if (pt != NULL) + return false; + + LLVM_TYPE_CONST llvm::StructType *st = + llvm::dyn_cast(type); + if (st != NULL) { + for (int i = 0; i < (int)st->getNumElements(); ++i) + if (lGenericTypeLayoutIndeterminate(st->getElementType(i))) + return true; + return false; + } + + type->dump(); + Assert(llvm::isa(type)); + return true; +} + + llvm::Value * Target::SizeOf(LLVM_TYPE_CONST llvm::Type *type, llvm::BasicBlock *insertAtEnd) { - if (isa == Target::GENERIC && type->isPrimitiveType() == false) { + if (isa == Target::GENERIC && + lGenericTypeLayoutIndeterminate(type)) { llvm::Value *index[1] = { LLVMInt32(1) }; LLVM_TYPE_CONST llvm::PointerType *ptrType = llvm::PointerType::get(type, 0); llvm::Value *voidPtr = llvm::ConstantPointerNull::get(ptrType); @@ -396,7 +432,8 @@ Target::SizeOf(LLVM_TYPE_CONST llvm::Type *type, llvm::Value * Target::StructOffset(LLVM_TYPE_CONST llvm::Type *type, int element, llvm::BasicBlock *insertAtEnd) { - if (isa == Target::GENERIC && type->isPrimitiveType() == false) { + if (isa == Target::GENERIC && + lGenericTypeLayoutIndeterminate(type) == true) { llvm::Value *indices[2] = { LLVMInt32(0), LLVMInt32(element) }; LLVM_TYPE_CONST llvm::PointerType *ptrType = llvm::PointerType::get(type, 0); llvm::Value *voidPtr = llvm::ConstantPointerNull::get(ptrType); From be0c77d556266af3c4ddc18ca313f99ddf68d9c7 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Sun, 8 Jan 2012 14:06:12 -0800 Subject: [PATCH 14/60] Detect more gather/scatter cases that are actually base+offsets. We now recognize patterns like (ptr + offset1 + offset2) as being cases we can handle with the base_offsets variants of the gather/scatter functions. (This can come up with multidimensional array indexing, for example.) Issue #150. --- opt.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/opt.cpp b/opt.cpp index 3510fbb2..c50f5f06 100644 --- a/opt.cpp +++ b/opt.cpp @@ -1063,7 +1063,8 @@ lGetConstantAddExprBaseOffset(llvm::Constant *op0, llvm::Constant *op1, *offsets with an int vector of the per-lane offsets */ static llvm::Value * -lGetBasePtrAndOffsets(llvm::Value *ptrs, llvm::Value **offsets) { +lGetBasePtrAndOffsets(llvm::Value *ptrs, llvm::Value **offsets, + llvm::Instruction *insertBefore) { llvm::Value *base = lGetBasePointer(ptrs); if (base != NULL) { // We have a straight up varying pointer with no indexing that's @@ -1079,12 +1080,20 @@ lGetBasePtrAndOffsets(llvm::Value *ptrs, llvm::Value **offsets) { if (bop != NULL && bop->getOpcode() == llvm::Instruction::Add) { // If we have a common pointer plus something, then we're also // good. - if ((base = lGetBasePointer(bop->getOperand(0))) != NULL) { - *offsets = bop->getOperand(1); + if ((base = lGetBasePtrAndOffsets(bop->getOperand(0), + offsets, insertBefore)) != NULL) { + *offsets = + llvm::BinaryOperator::Create(llvm::Instruction::Add, *offsets, + bop->getOperand(1), "new_offsets", + insertBefore); return base; } - else if ((base = lGetBasePointer(bop->getOperand(1))) != NULL) { - *offsets = bop->getOperand(0); + else if ((base = lGetBasePtrAndOffsets(bop->getOperand(1), + offsets, insertBefore)) != NULL) { + *offsets = + llvm::BinaryOperator::Create(llvm::Instruction::Add, *offsets, + bop->getOperand(0), "new_offsets", + insertBefore); return base; } } @@ -1316,7 +1325,8 @@ GatherScatterFlattenOpt::runOnBasicBlock(llvm::BasicBlock &bb) { // lGetBasePtrAndOffsets). llvm::Value *ptrs = callInst->getArgOperand(0); llvm::Value *offsetVector = NULL; - llvm::Value *basePtr = lGetBasePtrAndOffsets(ptrs, &offsetVector); + llvm::Value *basePtr = lGetBasePtrAndOffsets(ptrs, &offsetVector, + callInst); if (basePtr == NULL || offsetVector == NULL) // It's actually a fully general gather/scatter with a varying From fda00afe6eba57dbeb0902ad2c0c146533627091 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Sun, 8 Jan 2012 14:11:04 -0800 Subject: [PATCH 15/60] Rename .txt files in docs to .rst (which is what they actually are). --- docs/build.sh | 4 ++-- docs/{faq.txt => faq.rst} | 0 docs/{ispc.txt => ispc.rst} | 0 docs/{perf.txt => perf.rst} | 0 docs/{perfguide.txt => perfguide.rst} | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename docs/{faq.txt => faq.rst} (100%) rename docs/{ispc.txt => ispc.rst} (100%) rename docs/{perf.txt => perf.rst} (100%) rename docs/{perfguide.txt => perfguide.rst} (100%) diff --git a/docs/build.sh b/docs/build.sh index 904d2d21..f5700c38 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -2,11 +2,11 @@ for i in ispc perfguide faq; do rst2html.py --template=template.txt --link-stylesheet \ - --stylesheet-path=css/style.css $i.txt > $i.html + --stylesheet-path=css/style.css $i.rst > $i.html done rst2html.py --template=template-perf.txt --link-stylesheet \ - --stylesheet-path=css/style.css perf.txt > perf.html + --stylesheet-path=css/style.css perf.rst > perf.html #rst2latex --section-numbering --documentclass=article --documentoptions=DIV=9,10pt,letterpaper ispc.txt > ispc.tex #pdflatex ispc.tex diff --git a/docs/faq.txt b/docs/faq.rst similarity index 100% rename from docs/faq.txt rename to docs/faq.rst diff --git a/docs/ispc.txt b/docs/ispc.rst similarity index 100% rename from docs/ispc.txt rename to docs/ispc.rst diff --git a/docs/perf.txt b/docs/perf.rst similarity index 100% rename from docs/perf.txt rename to docs/perf.rst diff --git a/docs/perfguide.txt b/docs/perfguide.rst similarity index 100% rename from docs/perfguide.txt rename to docs/perfguide.rst From 86d88e9773231d0a80be8cc201d6ce587b92fd2a Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Sun, 8 Jan 2012 15:29:20 -0800 Subject: [PATCH 16/60] run_tests.py: fix to use multiple cores on windows, ignore non ispc inputs --- run_tests.py | 128 ++++++++++++++++++++++++++------------------------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/run_tests.py b/run_tests.py index 33b60b57..923265d4 100755 --- a/run_tests.py +++ b/run_tests.py @@ -16,6 +16,7 @@ import mutex import subprocess import shlex import platform +import tempfile is_windows = (platform.system() == 'Windows' or 'CYGWIN_NT' in platform.system()) @@ -72,7 +73,12 @@ if len(args) == 0: files = glob.glob("tests/*ispc") + glob.glob("failing_tests/*ispc") + \ glob.glob("tests_errors/*ispc") else: - files = args + files = [ ] + for f in args: + if os.path.splitext(string.lower(f))[1] != ".ispc": + print "Ignoring file %s, which doesn't have an .ispc extension." % f + else: + files += [ f ] # randomly shuffle the tests if asked to do so if (options.random): @@ -88,9 +94,8 @@ total_tests = 0 # http://bugs.python.org/issue5261. Therefore, we use the (deprecated but # still available) mutex class. #finished_tests_counter_lock = multiprocessing.Lock() -if not is_windows: - finished_tests_mutex = mutex.mutex() - finished_tests_counter = multiprocessing.Value(c_int) +finished_tests_mutex = mutex.mutex() +finished_tests_counter = multiprocessing.Value(c_int) # utility routine to print an update on the number of tests that have been # finished. Should be called with the mutex (or lock) held.. @@ -146,16 +151,22 @@ def run_cmds(compile_cmds, run_cmd, filename, expect_failure): def run_test(filename): + global is_windows + if is_windows: + input_prefix = "../" + else: + input_prefix = "" + # is this a test to make sure an error is issued? want_error = (filename.find("tests_errors") != -1) if want_error == True: ispc_cmd = "ispc --werror --nowrap %s --arch=%s --target=%s" % \ - (filename, options.arch, options.target) + (input_prefix + filename, options.arch, options.target) (return_code, output) = run_command(ispc_cmd) got_error = (return_code != 0) # figure out the error message we're expecting - file = open(filename, 'r') + file = open(input_prefix + filename, 'r') firstline = file.readline() firstline = string.replace(firstline, "//", "") firstline = string.lstrip(firstline) @@ -180,7 +191,7 @@ def run_test(filename): # function that this test has. sig2def = { "f_v(" : 0, "f_f(" : 1, "f_fu(" : 2, "f_fi(" : 3, "f_du(" : 4, "f_duf(" : 5, "f_di(" : 6 } - file = open(filename, 'r') + file = open(input_prefix + filename, 'r') match = -1 for line in file: # look for lines with 'export'... @@ -202,14 +213,13 @@ def run_test(filename): if is_generic_target: obj_name = "%s.cpp" % filename - global is_windows if is_windows: if not is_generic_target: - obj_name = "%s.obj" % filename - exe_name = "%s.exe" % filename + obj_name = "%s%s.obj" % (input_prefix, filename) + exe_name = "%s%s.exe" % (input_prefix, filename) - cc_cmd = "%s /I. /Zi /nologo /DTEST_SIG=%d test_static.cpp %s /Fe%s" % \ - (options.compiler_exe, match, obj_name, exe_name) + cc_cmd = "%s /I. /Zi /nologo /DTEST_SIG=%d %stest_static.cpp %s /Fe%s" % \ + (options.compiler_exe, match, input_prefix, obj_name, exe_name) if should_fail: cc_cmd += " /DEXPECT_FAILURE" else: @@ -229,7 +239,7 @@ def run_test(filename): cc_cmd += " -DEXPECT_FAILURE" ispc_cmd = "ispc --woff %s -o %s --arch=%s --target=%s" % \ - (filename, obj_name, options.arch, options.target) + (input_prefix+filename, obj_name, options.arch, options.target) if options.no_opt: ispc_cmd += " -O0" if is_generic_target: @@ -257,12 +267,28 @@ def run_test(filename): # this function will be running in parallel across all of the CPU cores of # the system. def run_tasks_from_queue(queue, queue_ret): + if is_windows: + tmpdir = "tmp%d" % os.getpid() + os.mkdir(tmpdir) + os.chdir(tmpdir) + else: + olddir = "" + compile_error_files = [ ] run_error_files = [ ] while True: filename = queue.get() if (filename == 'STOP'): queue_ret.put((compile_error_files, run_error_files)) + if is_windows: + try: + os.remove("test_static.obj") + os.remove("/vc100.pdb") + os.chdir("..") + os.rmdir(tmpdir) + except: + None + sys.exit(0) (compile_error, run_error) = run_test(filename) @@ -289,61 +315,37 @@ if __name__ == '__main__': compile_error_files = [ ] run_error_files = [ ] - if is_windows: - # cl.exe gets itself all confused if we have multiple instances of - # it running concurrently and operating on the same .cpp file - # (test_static.cpp), even if we are generating a differently-named - # exe in the end. So run serially. :-( - nthreads = 1 - num_done = 0 - print "Running %d tests." % (total_tests) - for fn in files: - (compile_error, run_error) = run_test(fn) - if compile_error != 0: - compile_error_files += fn - if run_error != 0: - run_error_files += fn + nthreads = multiprocessing.cpu_count() + print "Found %d CPUs. Running %d tests." % (nthreads, total_tests) - num_done += 1 - progress_str = " Done %d / %d [%s]" % (num_done, total_tests, fn) - # spaces to clear out detrius from previous printing... - for x in range(30): - progress_str += ' ' - progress_str += '\r' - sys.stdout.write(progress_str) - sys.stdout.flush() - else: - nthreads = multiprocessing.cpu_count() - print "Found %d CPUs. Running %d tests." % (nthreads, total_tests) + # put each of the test filenames into a queue + q = multiprocessing.Queue() + for fn in files: + q.put(fn) + for x in range(nthreads): + q.put('STOP') + qret = multiprocessing.Queue() - # put each of the test filenames into a queue - q = multiprocessing.Queue() - for fn in files: - q.put(fn) - for x in range(nthreads): - q.put('STOP') - qret = multiprocessing.Queue() + # need to catch sigint so that we can terminate all of the tasks if + # we're interrupted + signal.signal(signal.SIGINT, sigint) - # need to catch sigint so that we can terminate all of the tasks if - # we're interrupted - signal.signal(signal.SIGINT, sigint) + # launch jobs to run tests + for x in range(nthreads): + t = multiprocessing.Process(target=run_tasks_from_queue, args=(q,qret)) + task_threads.append(t) + t.start() - # launch jobs to run tests - for x in range(nthreads): - t = multiprocessing.Process(target=run_tasks_from_queue, args=(q,qret)) - task_threads.append(t) - t.start() + # wait for them to all finish and then return the number that failed + # (i.e. return 0 if all is ok) + for t in task_threads: + t.join() + print - # wait for them to all finish and then return the number that failed - # (i.e. return 0 if all is ok) - for t in task_threads: - t.join() - print - - while not qret.empty(): - (c, r) = qret.get() - compile_error_files += c - run_error_files += r + while not qret.empty(): + (c, r) = qret.get() + compile_error_files += c + run_error_files += r if len(compile_error_files) > 0: compile_error_files.sort() From 002f27a30f7d862ce6a45cd08aa5f70ede8d9fba Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Lacaze Date: Mon, 9 Jan 2012 09:44:58 +0100 Subject: [PATCH 17/60] Implement vasprintf and asprintf for platforms lacking them. --- stmt.cpp | 13 ------------- util.cpp | 53 ++++++++++++++++++++++++++--------------------------- util.h | 9 +++++++++ 3 files changed, 35 insertions(+), 40 deletions(-) diff --git a/stmt.cpp b/stmt.cpp index 043f6fa2..079919b2 100644 --- a/stmt.cpp +++ b/stmt.cpp @@ -2299,16 +2299,6 @@ AssertStmt::EmitCode(FunctionEmitContext *ctx) const { m->module->getFunction("__do_assert_varying"); Assert(assertFunc != NULL); -#ifdef ISPC_IS_WINDOWS - char errorString[2048]; - if (sprintf_s(errorString, sizeof(errorString), - "%s(%d): Assertion failed: %s\n", pos.name, - pos.first_line, message.c_str()) == -1) { - Error(pos, "Fatal error in sprintf_s() call when generating assert " - "string."); - return; - } -#else char *errorString; if (asprintf(&errorString, "%s:%d:%d: Assertion failed: %s\n", pos.name, pos.first_line, pos.first_column, @@ -2317,7 +2307,6 @@ AssertStmt::EmitCode(FunctionEmitContext *ctx) const { "unable to allocate memory!"); return; } -#endif std::vector args; args.push_back(ctx->GetStringPtr(errorString)); @@ -2325,9 +2314,7 @@ AssertStmt::EmitCode(FunctionEmitContext *ctx) const { args.push_back(ctx->GetFullMask()); ctx->CallInst(assertFunc, NULL, args, ""); -#ifndef ISPC_IS_WINDOWS free(errorString); -#endif // !ISPC_IS_WINDOWS } diff --git a/util.cpp b/util.cpp index 008fc5a3..f0d89c0d 100644 --- a/util.cpp +++ b/util.cpp @@ -187,6 +187,32 @@ lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) { } +#ifdef ISPC_IS_WINDOWS +// we cover for the lack vasprintf and asprintf on windows (also covers mingw) +int +vasprintf(char **sptr, const char *fmt, va_list argv) +{ + int wanted = vsnprintf(*sptr = NULL, 0, fmt, argv); + if((wanted < 0) || ((*sptr = (char*)malloc( 1 + wanted )) == NULL)) + return -1; + + return vsprintf(*sptr, fmt, argv); +} + + +int +asprintf(char **sptr, const char *fmt, ...) +{ + int retval; + va_list argv; + va_start(argv, fmt); + retval = vasprintf(sptr, fmt, argv); + va_end(argv); + return retval; +} +#endif + + /** Helper function for Error(), Warning(), etc. @param type The type of message being printed (e.g. "Warning") @@ -197,30 +223,6 @@ lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) { */ static void lPrint(const char *type, SourcePos p, const char *fmt, va_list args) { -#ifdef ISPC_IS_WINDOWS - char errorBuf[2048], formattedBuf[2048]; - if (vsnprintf_s(errorBuf, sizeof(errorBuf), _TRUNCATE, fmt, args) == -1) { - fprintf(stderr, "vsnprintf_s() error!\n"); - return; - } - - if (p.first_line == 0) { - // We don't have a valid SourcePos, so create a message without it - if (sprintf_s(formattedBuf, sizeof(formattedBuf), "%s: %s\n", - type, errorBuf) == -1) { - fprintf(stderr, "vsnprintf_s() error!\n"); - exit(1); - } - } - else { - // Create an error message that includes the file and line number - if (sprintf_s(formattedBuf, sizeof(formattedBuf), "%s(%d): %s: %s\n", - p.name, p.first_line, type, errorBuf) == -1) { - fprintf(stderr, "vsnprintf_s() error!\n"); - exit(1); - } - } -#else char *errorBuf, *formattedBuf; if (vasprintf(&errorBuf, fmt, args) == -1) { fprintf(stderr, "vasprintf() unable to allocate memory!\n"); @@ -241,7 +243,6 @@ lPrint(const char *type, SourcePos p, const char *fmt, va_list args) { exit(1); } } -#endif // Now that we've done all that work, see if we've already printed the // exact same error message. If so, return, so we don't redundantly @@ -254,10 +255,8 @@ lPrint(const char *type, SourcePos p, const char *fmt, va_list args) { lPrintWithWordBreaks(formattedBuf, lTerminalWidth(), stderr); lPrintFileLineContext(p); -#ifndef ISPC_IS_WINDOWS free(errorBuf); free(formattedBuf); -#endif // !ISPC_IS_WINDOWS } diff --git a/util.h b/util.h index 874e5bdb..e7575379 100644 --- a/util.h +++ b/util.h @@ -40,6 +40,9 @@ #define ISPC_UTIL_H #include "ispc.h" +#ifdef ISPC_IS_WINDOWS +#include +#endif struct SourcePos; @@ -62,6 +65,12 @@ inline uint32_t RoundUpPow2(uint32_t v) { #define PRINTF_FUNC #endif // __GNUG__ +// for cross-platform compatibility +#ifdef ISPC_IS_WINDOWS +int vasprintf(char **sptr, const char *fmt, va_list argv); +int asprintf(char **sptr, const char *fmt, ...); +#endif + /** Prints a debugging message. These messages are only printed if g->debugPrint is \c true. In addition to a program source code position to associate with the message, a printf()-style format string From d84cf781daf1fd112f766fcddbffd7aa413aebc5 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Lacaze Date: Mon, 9 Jan 2012 09:45:40 +0100 Subject: [PATCH 18/60] Mingw does not have sysconf, use the msc way of finding processors. --- builtins/builtins.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/builtins.c b/builtins/builtins.c index f1cb35dd..36498e1a 100644 --- a/builtins/builtins.c +++ b/builtins/builtins.c @@ -149,7 +149,7 @@ void __do_print(const char *format, const char *types, int width, int mask, int __num_cores() { -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(__MINGW32__) // This is quite a hack. Including all of windows.h to get this definition // pulls in a bunch of stuff that leads to undefined symbols at link time. // So we don't #include but instead have the equivalent declarations From 54e8e8022b02edd75b96cab4d92a484bac0fcaa6 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Lacaze Date: Mon, 9 Jan 2012 10:18:39 +0100 Subject: [PATCH 19/60] suppress warnings about long long arguments --- cbackend.cpp | 7 ++++++- expr.cpp | 19 +++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/cbackend.cpp b/cbackend.cpp index 2eb853e3..5fe87264 100644 --- a/cbackend.cpp +++ b/cbackend.cpp @@ -16,6 +16,11 @@ #warning "The C++ backend isn't supported when building with LLVM 2.9" #else +#include +#ifndef PRIx64 +#define PRIx64 "llx" +#endif + #include "llvm/CallingConv.h" #include "llvm/Constants.h" #include "llvm/DerivedTypes.h" @@ -1280,7 +1285,7 @@ void CWriter::printConstant(Constant *CPV, bool Static) { char Buffer[100]; uint64_t ll = DoubleToBits(V); - sprintf(Buffer, "0x%llx", static_cast(ll)); + sprintf(Buffer, "0x%"PRIx64, static_cast(ll)); std::string Num(&Buffer[0], &Buffer[6]); unsigned long Val = strtoul(Num.c_str(), 0, 16); diff --git a/expr.cpp b/expr.cpp index 1bcfa70d..f94245d5 100644 --- a/expr.cpp +++ b/expr.cpp @@ -43,6 +43,13 @@ #include "module.h" #include "util.h" #include "llvmutil.h" +#include +#ifndef PRId64 +#define PRId64 "lld" +#endif +#ifndef PRIu64 +#define PRIu64 "llu" +#endif #include #include @@ -4540,18 +4547,10 @@ ConstExpr::Print() const { printf("%f", floatVal[i]); break; case AtomicType::TYPE_INT64: -#ifdef ISPC_IS_LINUX - printf("%ld", int64Val[i]); -#else - printf("%lld", int64Val[i]); -#endif + printf("%"PRId64, int64Val[i]); break; case AtomicType::TYPE_UINT64: -#ifdef ISPC_IS_LINUX - printf("%lu", uint64Val[i]); -#else - printf("%llu", uint64Val[i]); -#endif + printf("%"PRIu64, uint64Val[i]); break; case AtomicType::TYPE_DOUBLE: printf("%f", doubleVal[i]); From da9200fcee8756b510083ca5434da87fc6512f28 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Lacaze Date: Mon, 9 Jan 2012 10:19:09 +0100 Subject: [PATCH 20/60] Fix alloca use on mingw. --- opt.cpp | 4 +++- util.cpp | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/opt.cpp b/opt.cpp index c50f5f06..c66c69b1 100644 --- a/opt.cpp +++ b/opt.cpp @@ -79,7 +79,9 @@ #include #elif defined(ISPC_IS_WINDOWS) #include - #define alloca _alloca + #ifndef __MINGW32__ + #define alloca _alloca + #endif #endif // ISPC_IS_WINDOWS static llvm::Pass *CreateIntrinsicsOptPass(); diff --git a/util.cpp b/util.cpp index f0d89c0d..34798a35 100644 --- a/util.cpp +++ b/util.cpp @@ -39,6 +39,9 @@ #include "module.h" #ifdef ISPC_IS_WINDOWS #include +#ifdef __MINGW32__ +#include // for alloca() +#endif #else #include #endif From a0e9793de31c87bc610c91c32bbc957178d1a972 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Lacaze Date: Mon, 9 Jan 2012 10:19:46 +0100 Subject: [PATCH 21/60] Shut up warning wrt CONSOLE_SCREEN_BUFFER_INFO initialization --- util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.cpp b/util.cpp index 34798a35..9c802f19 100644 --- a/util.cpp +++ b/util.cpp @@ -78,7 +78,7 @@ lTerminalWidth() { HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); if (h == INVALID_HANDLE_VALUE || h == NULL) return 80; - CONSOLE_SCREEN_BUFFER_INFO bufferInfo = { 0 }; + CONSOLE_SCREEN_BUFFER_INFO bufferInfo = { {0} }; GetConsoleScreenBufferInfo(h, &bufferInfo); return bufferInfo.dwSize.X; #else From 0be1b70fba4fec0da9a01bc9bc45d3d1ba574fea Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Lacaze Date: Mon, 9 Jan 2012 10:28:52 +0100 Subject: [PATCH 22/60] Mingw has strtoull, make use of it. --- lex.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lex.ll b/lex.ll index 39194767..1b6d382b 100644 --- a/lex.ll +++ b/lex.ll @@ -155,7 +155,7 @@ L?\"(\\.|[^\\"])*\" { lStringConst(yylval, yylloc); return TOKEN_STRING_LITERAL; if (yytext[0] == '0' && yytext[1] == 'b') yylval->intVal = lParseBinary(yytext+2, *yylloc, &endPtr); else { -#ifdef ISPC_IS_WINDOWS +#if defined(ISPC_IS_WINDOWS) && !defined(__MINGW32__) yylval->intVal = _strtoi64(yytext, &endPtr, 0); #else // FIXME: should use strtouq and then issue an error if we can't From d8728104b40acc369f7a49c3775b386cf740fa6d Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Lacaze Date: Mon, 9 Jan 2012 10:29:16 +0100 Subject: [PATCH 23/60] Handle the case whereby BUILD_DATE is already defined. --- main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.cpp b/main.cpp index 84c5c142..d004815b 100644 --- a/main.cpp +++ b/main.cpp @@ -54,7 +54,9 @@ #ifdef ISPC_IS_WINDOWS #define strcasecmp stricmp +#ifndef BUILD_DATE #define BUILD_DATE __DATE__ +#endif #define BUILD_VERSION "" #endif // ISPC_IS_WINDOWS From 2654bb0112f7fd521a5eead38bb0f59886bb4de4 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Lacaze Date: Mon, 9 Jan 2012 10:29:54 +0100 Subject: [PATCH 24/60] Handle python installations in non-standards locations. --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index f9d0cbab..9fbbbb72 100644 --- a/Makefile +++ b/Makefile @@ -133,22 +133,22 @@ objs/lex.o: objs/lex.cpp $(HEADERS) objs/parse.cc objs/builtins-%.cpp: builtins/%.ll builtins/util.m4 $(wildcard builtins/*common.ll) @echo Creating C++ source from builtins definition file $< - @m4 -Ibuiltins/ -DLLVM_VERSION=$(LLVM_VERSION) $< | ./bitcode2cpp.py $< > $@ + @m4 -Ibuiltins/ -DLLVM_VERSION=$(LLVM_VERSION) $< | python bitcode2cpp.py $< > $@ objs/builtins-c-32.cpp: builtins/builtins.c @echo Creating C++ source from builtins definition file $< - @$(CLANG) -m32 -emit-llvm -c $< -o - | llvm-dis - | ./bitcode2cpp.py c-32 > $@ + @$(CLANG) -m32 -emit-llvm -c $< -o - | llvm-dis - | python bitcode2cpp.py c-32 > $@ objs/builtins-c-64.cpp: builtins/builtins.c @echo Creating C++ source from builtins definition file $< - @$(CLANG) -m64 -emit-llvm -c $< -o - | llvm-dis - | ./bitcode2cpp.py c-64 > $@ + @$(CLANG) -m64 -emit-llvm -c $< -o - | llvm-dis - | python bitcode2cpp.py c-64 > $@ objs/stdlib_generic_ispc.cpp: stdlib.ispc @echo Creating C++ source from $< for generic @$(CLANG) -E -x c -DISPC_TARGET_GENERIC=1 -DISPC=1 -DPI=3.1415926536 $< -o - | \ - ./stdlib2cpp.py generic > $@ + python stdlib2cpp.py generic > $@ objs/stdlib_x86_ispc.cpp: stdlib.ispc @echo Creating C++ source from $< for x86 @$(CLANG) -E -x c -DISPC=1 -DPI=3.1415926536 $< -o - | \ - ./stdlib2cpp.py x86 > $@ + python stdlib2cpp.py x86 > $@ From b683aa11b1640fdd6618d4ce3f852cbe41e4cb19 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Lacaze Date: Mon, 9 Jan 2012 10:52:46 +0100 Subject: [PATCH 25/60] Fix linking under mingw, libdl is Linux only. --- Makefile | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9fbbbb72..4fc142a2 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ # ARCH_OS = $(shell uname) +ARCH_OS2 = $(shell uname -o) ARCH_TYPE = $(shell arch) ifeq ($(shell llvm-config --version), 3.1svn) @@ -26,7 +27,15 @@ CLANG_LIBS = -lclangFrontend -lclangDriver \ -lclangAnalysis -lclangAST -lclangLex -lclangBasic ISPC_LIBS=$(shell llvm-config --ldflags) $(CLANG_LIBS) $(LLVM_LIBS) \ - -lpthread -ldl + -lpthread + +ifeq ($(ARCH_OS),Linux) + ISPC_LIBS += -ldl +endif + +ifeq ($(ARCH_OS2),Msys) + ISPC_LIBS += -lshlwapi -limagehlp -lpsapi +endif LLVM_CXXFLAGS=$(shell llvm-config --cppflags) LLVM_VERSION=LLVM_$(shell llvm-config --version | sed s/\\./_/) From 2be1251c70993f36a426dbbcb889881f95d1f909 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Mon, 9 Jan 2012 07:40:47 -0800 Subject: [PATCH 26/60] Fix Makefile on OSX (uname -o not supported) --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4fc142a2..25ae845f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,11 @@ # ARCH_OS = $(shell uname) -ARCH_OS2 = $(shell uname -o) +ifeq ($(ARCH_OS), Darwin) + ARCH_OS2 = "OSX" +else + ARCH_OS2 = $(shell uname -o) +endif ARCH_TYPE = $(shell arch) ifeq ($(shell llvm-config --version), 3.1svn) From 5134de71c0d6d243ecef7cd04390ce0356e65d69 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Mon, 9 Jan 2012 09:05:20 -0800 Subject: [PATCH 27/60] Fix Windows build (inttypes.h not available) --- cbackend.cpp | 3 +++ expr.cpp | 2 ++ 2 files changed, 5 insertions(+) diff --git a/cbackend.cpp b/cbackend.cpp index 5fe87264..96f54c23 100644 --- a/cbackend.cpp +++ b/cbackend.cpp @@ -16,7 +16,10 @@ #warning "The C++ backend isn't supported when building with LLVM 2.9" #else +#ifndef _MSC_VER #include +#endif + #ifndef PRIx64 #define PRIx64 "llx" #endif diff --git a/expr.cpp b/expr.cpp index f94245d5..c424f3c2 100644 --- a/expr.cpp +++ b/expr.cpp @@ -43,7 +43,9 @@ #include "module.h" #include "util.h" #include "llvmutil.h" +#ifndef _MSC_VER #include +#endif #ifndef PRId64 #define PRId64 "lld" #endif From f4653ecd1135d52987435527fbc8bbb919243eab Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Mon, 9 Jan 2012 16:05:40 -0800 Subject: [PATCH 28/60] Release notes for 1.1.2 and doxygen version number bump --- docs/ReleaseNotes.txt | 21 +++++++++++++++++++++ doxygen.cfg | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/ReleaseNotes.txt b/docs/ReleaseNotes.txt index eab5a105..0d354f67 100644 --- a/docs/ReleaseNotes.txt +++ b/docs/ReleaseNotes.txt @@ -1,3 +1,24 @@ +=== v1.1.2 === (9 January 2012) + +The major new feature in this release is support for "generic" C++ +vectorized output; in other words, ispc can emit C++ code that corresponds +to the vectorized computation that the ispc program represents. See the +examples/intrinsics directory in the ispc distribution for two example +implementations of the set of functions that must be provided map the +vector calls generated by ispc to target specific functions. + +ispc now has partial support for 'goto' statements; specifically, goto is +allowed if any enclosing control flow statements (if/for/while/do) have +'uniform' test expressions, but not if they have 'varying' tests. + +A number of improvements have been made to the code generated for gathers +and scatters--one of them (better matching x86's "free" scale by 2/4/8 for +addressing calculations) improved the performance of the noise example by +14%. + +Many small bugs have been fixed in this release as well, including issue +numbers 138, 129, 135, 127, 149, and 142. + === v1.1.1 === (15 December 2011) This release doesn't include any significant new functionality, but does diff --git a/doxygen.cfg b/doxygen.cfg index dc633b48..7a1f37d0 100644 --- a/doxygen.cfg +++ b/doxygen.cfg @@ -31,7 +31,7 @@ PROJECT_NAME = "Intel SPMD Program Compiler" # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 1.1.1 +PROJECT_NUMBER = 1.1.2 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. From 8a8e1a7f73f00ecda9eb68134ee742f262272ffb Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Tue, 10 Jan 2012 16:50:13 -0800 Subject: [PATCH 29/60] Fix bug with multiple EmitCode() calls due to missing braces. In short, we were inadvertently trying to emit each function's code a second time if the function had a mask check at the start of it. StmtList::EmitCode() was covering this error up by not emitting code if the current basic block is NULL. --- func.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/func.cpp b/func.cpp index 6f5e03db..26ea83ed 100644 --- a/func.cpp +++ b/func.cpp @@ -334,11 +334,12 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function, if (ctx->GetCurrentBasicBlock()) ctx->ReturnInst(); } - else + else { // Set up basic blocks for goto targets ctx->InitializeLabelMap(code); // No check, just emit the code code->EmitCode(ctx); + } } if (ctx->GetCurrentBasicBlock()) { From 0223bb85ee0bcc1ca1b71fd7f40ef2244b611a25 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Wed, 11 Jan 2012 09:14:39 -0800 Subject: [PATCH 30/60] Fix bug in StmtList::EmitCode() Previously, we would return immediately if the current basic block was NULL; however, this is the wrong thing to do in that goto labels and case/default labels in switch statements will establish a new current basic block even if the current one is NULL. --- stmt.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/stmt.cpp b/stmt.cpp index 079919b2..783d0a5d 100644 --- a/stmt.cpp +++ b/stmt.cpp @@ -2046,9 +2046,6 @@ LabeledStmt::EstimateCost() const { void StmtList::EmitCode(FunctionEmitContext *ctx) const { - if (!ctx->GetCurrentBasicBlock()) - return; - ctx->StartScope(); ctx->SetDebugPos(pos); for (unsigned int i = 0; i < stmts.size(); ++i) From 9670ab08874057c37da2d15fb6f1d8339ffa343b Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Wed, 11 Jan 2012 09:09:40 -0800 Subject: [PATCH 31/60] Add missing cases to watch out for in lCheckAllOffSafety() Previously, we weren't checking for member expressions that dereferenced a pointer or pointer dereference expressions--only array indexing! --- expr.h | 1 - stmt.cpp | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/expr.h b/expr.h index c66b7754..48388475 100644 --- a/expr.h +++ b/expr.h @@ -314,7 +314,6 @@ public: std::string identifier; const SourcePos identifierPos; -protected: MemberExpr(Expr *expr, const char *identifier, SourcePos pos, SourcePos identifierPos, bool derefLValue); diff --git a/stmt.cpp b/stmt.cpp index 783d0a5d..5f23c98f 100644 --- a/stmt.cpp +++ b/stmt.cpp @@ -695,6 +695,23 @@ lCheckAllOffSafety(ASTNode *node, void *data) { } // All indices are in-bounds + return true; + } + + MemberExpr *me; + if ((me = dynamic_cast(node)) != NULL && + me->dereferenceExpr) { + *okPtr = false; + return false; + } + + DereferenceExpr *de; + if ((de = dynamic_cast(node)) != NULL) { + const Type *exprType = de->expr->GetType(); + if (dynamic_cast(exprType) != NULL) { + *okPtr = false; + return false; + } } return true; From b67446d998dc533d9462f2b21e4a93c3cce3178d Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Wed, 11 Jan 2012 09:15:05 -0800 Subject: [PATCH 32/60] 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. --- ast.cpp | 11 + ctx.cpp | 539 +++++++++++++++++++++++++++++++------ ctx.h | 93 ++++++- docs/ispc.rst | 26 ++ ispc.h | 2 + parse.yy | 14 +- stmt.cpp | 301 +++++++++++++++++++++ stmt.h | 59 ++++ tests/switch-1.ispc | 18 ++ tests/switch-10.ispc | 44 +++ tests/switch-11.ispc | 50 ++++ tests/switch-12.ispc | 54 ++++ tests/switch-2.ispc | 17 ++ tests/switch-3.ispc | 18 ++ tests/switch-4.ispc | 24 ++ tests/switch-5.ispc | 22 ++ tests/switch-6.ispc | 27 ++ tests/switch-7.ispc | 32 +++ tests/switch-8.ispc | 36 +++ tests/switch-9.ispc | 34 +++ tests_errors/switch-1.ispc | 9 + tests_errors/switch-2.ispc | 12 + tests_errors/switch-3.ispc | 13 + tests_errors/switch-4.ispc | 13 + tests_errors/switch-5.ispc | 14 + tests_errors/switch-6.ispc | 12 + 26 files changed, 1402 insertions(+), 92 deletions(-) create mode 100644 tests/switch-1.ispc create mode 100644 tests/switch-10.ispc create mode 100644 tests/switch-11.ispc create mode 100644 tests/switch-12.ispc create mode 100644 tests/switch-2.ispc create mode 100644 tests/switch-3.ispc create mode 100644 tests/switch-4.ispc create mode 100644 tests/switch-5.ispc create mode 100644 tests/switch-6.ispc create mode 100644 tests/switch-7.ispc create mode 100644 tests/switch-8.ispc create mode 100644 tests/switch-9.ispc create mode 100644 tests_errors/switch-1.ispc create mode 100644 tests_errors/switch-2.ispc create mode 100644 tests_errors/switch-3.ispc create mode 100644 tests_errors/switch-4.ispc create mode 100644 tests_errors/switch-5.ispc create mode 100644 tests_errors/switch-6.ispc diff --git a/ast.cpp b/ast.cpp index f260c0b5..746bc0ec 100644 --- a/ast.cpp +++ b/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(node)) != NULL) + cs->stmts = (Stmt *)WalkAST(cs->stmts, preFunc, postFunc, data); + else if ((defs = dynamic_cast(node)) != NULL) + defs->stmts = (Stmt *)WalkAST(defs->stmts, preFunc, postFunc, data); + else if ((ss = dynamic_cast(node)) != NULL) { + ss->expr = (Expr *)WalkAST(ss->expr, preFunc, postFunc, data); + ss->stmts = (Stmt *)WalkAST(ss->stmts, preFunc, postFunc, data); + } else if (dynamic_cast(node) != NULL || dynamic_cast(node) != NULL || dynamic_cast(node) != NULL) { diff --git a/ctx.cpp b/ctx.cpp index b0afca64..3d5a8d9d 100644 --- a/ctx.cpp +++ b/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 > *bbCases, + const std::map *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 > *savedCaseBlocks; + const std::map *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 > *bbc = NULL, + const std::map *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 > *savedCases, + const std::map *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 > &bbCases, + const std::map &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 >(bbCases); + nextBlocks = new std::map(bbNext); + + if (inUniformSwitch()) { + // For a uniform switch, just wire things up to the LLVM switch + // instruction. + Assert(llvm::isa(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::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; +} diff --git a/ctx.h b/ctx.h index 743dbb67..88533ce2 100644 --- a/ctx.h +++ b/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 > &caseBlocks, + const std::map &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 > *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 *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); diff --git a/docs/ispc.rst b/docs/ispc.rst index 32e5017c..2caf9c4c 100644 --- a/docs/ispc.rst +++ b/docs/ispc.rst @@ -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" ---------------------------------------------------- diff --git a/ispc.h b/ispc.h index e058cc3b..009470e2 100644 --- a/ispc.h +++ b/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, diff --git a/parse.yy b/parse.yy index 07575948..671e426f 100644 --- a/parse.yy +++ b/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 diff --git a/stmt.cpp b/stmt.cpp index 5f23c98f..ae28ac67 100644 --- a/stmt.cpp +++ b/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 > 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 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(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(node); + DefaultStmt *ds = dynamic_cast(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 diff --git a/stmt.h b/stmt.h index eb5af2f9..8b22603a 100644 --- a/stmt.h +++ b/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; }; diff --git a/tests/switch-1.ispc b/tests/switch-1.ispc new file mode 100644 index 00000000..7ab2c7d8 --- /dev/null +++ b/tests/switch-1.ispc @@ -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; +} diff --git a/tests/switch-10.ispc b/tests/switch-10.ispc new file mode 100644 index 00000000..abd94d7c --- /dev/null +++ b/tests/switch-10.ispc @@ -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; +} diff --git a/tests/switch-11.ispc b/tests/switch-11.ispc new file mode 100644 index 00000000..daacdf76 --- /dev/null +++ b/tests/switch-11.ispc @@ -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; +} diff --git a/tests/switch-12.ispc b/tests/switch-12.ispc new file mode 100644 index 00000000..9a803012 --- /dev/null +++ b/tests/switch-12.ispc @@ -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; +} diff --git a/tests/switch-2.ispc b/tests/switch-2.ispc new file mode 100644 index 00000000..0f4a78fc --- /dev/null +++ b/tests/switch-2.ispc @@ -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; +} diff --git a/tests/switch-3.ispc b/tests/switch-3.ispc new file mode 100644 index 00000000..1b8b1a6e --- /dev/null +++ b/tests/switch-3.ispc @@ -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; +} diff --git a/tests/switch-4.ispc b/tests/switch-4.ispc new file mode 100644 index 00000000..81f715e2 --- /dev/null +++ b/tests/switch-4.ispc @@ -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; +} diff --git a/tests/switch-5.ispc b/tests/switch-5.ispc new file mode 100644 index 00000000..dfa72434 --- /dev/null +++ b/tests/switch-5.ispc @@ -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; +} diff --git a/tests/switch-6.ispc b/tests/switch-6.ispc new file mode 100644 index 00000000..8f13ad65 --- /dev/null +++ b/tests/switch-6.ispc @@ -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; +} diff --git a/tests/switch-7.ispc b/tests/switch-7.ispc new file mode 100644 index 00000000..bd0eefec --- /dev/null +++ b/tests/switch-7.ispc @@ -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; +} diff --git a/tests/switch-8.ispc b/tests/switch-8.ispc new file mode 100644 index 00000000..db46670f --- /dev/null +++ b/tests/switch-8.ispc @@ -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; +} diff --git a/tests/switch-9.ispc b/tests/switch-9.ispc new file mode 100644 index 00000000..9bfd0d03 --- /dev/null +++ b/tests/switch-9.ispc @@ -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; +} diff --git a/tests_errors/switch-1.ispc b/tests_errors/switch-1.ispc new file mode 100644 index 00000000..b38dd621 --- /dev/null +++ b/tests_errors/switch-1.ispc @@ -0,0 +1,9 @@ +// Case statement value must be a compile-time integer constant + +void foo(float f) { + switch (f) { + case 1.5: + ++f; + } +} + diff --git a/tests_errors/switch-2.ispc b/tests_errors/switch-2.ispc new file mode 100644 index 00000000..7a90ff98 --- /dev/null +++ b/tests_errors/switch-2.ispc @@ -0,0 +1,12 @@ +// Duplicate case value "1" + +void foo(float f) { + switch (f) { + case 1: + ++f; + case 2: + case 1: + f = 0; + } +} + diff --git a/tests_errors/switch-3.ispc b/tests_errors/switch-3.ispc new file mode 100644 index 00000000..3b8adf67 --- /dev/null +++ b/tests_errors/switch-3.ispc @@ -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; +} + diff --git a/tests_errors/switch-4.ispc b/tests_errors/switch-4.ispc new file mode 100644 index 00000000..f20955bd --- /dev/null +++ b/tests_errors/switch-4.ispc @@ -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; + } +} + diff --git a/tests_errors/switch-5.ispc b/tests_errors/switch-5.ispc new file mode 100644 index 00000000..350d21c0 --- /dev/null +++ b/tests_errors/switch-5.ispc @@ -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; + } +} + diff --git a/tests_errors/switch-6.ispc b/tests_errors/switch-6.ispc new file mode 100644 index 00000000..fab2236c --- /dev/null +++ b/tests_errors/switch-6.ispc @@ -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; + } +} + From 602209e5a8cf60316d9544f0d0e79e67fed09739 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Thu, 12 Jan 2012 05:55:42 -0800 Subject: [PATCH 33/60] Tiny updates to documentation, comment for switch stuff. --- docs/ispc.rst | 3 +-- stmt.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/ispc.rst b/docs/ispc.rst index 2caf9c4c..ddac4bcd 100644 --- a/docs/ispc.rst +++ b/docs/ispc.rst @@ -1142,7 +1142,7 @@ in C: * Expression syntax and basic types * Syntax for variable declarations -* Control flow structures: if, for, while, do +* Control flow structures: ``if``, ``for``, ``while``, ``do``, and ``switch``. * Pointers, including function pointers, ``void *``, and C's array/pointer duality (arrays are converted to pointers when passed to functions, etc.) * Structs and arrays @@ -1186,7 +1186,6 @@ but are likely to be supported in future releases: ``int64`` types * Character constants * String constants and arrays of characters as strings -* ``switch`` statements * ``goto`` statements are partially supported (see `Unstructured Control Flow: "goto"`_) * ``union`` types * Bitfield members of ``struct`` types diff --git a/stmt.cpp b/stmt.cpp index ae28ac67..acc36aaa 100644 --- a/stmt.cpp +++ b/stmt.cpp @@ -2156,7 +2156,7 @@ SwitchStmt::TypeCheck() { // 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. + // https://github.com/ispc/ispc/issues/156 for details. bool isUniform = (exprType->IsUniformType() && lHasVaryingBreakOrContinue(stmts) == false); From 652215861e30897fb5de4090cee4d150203cb7d9 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Thu, 12 Jan 2012 08:37:18 -0800 Subject: [PATCH 34/60] Update dynamic target dispatch code to support AVX2. --- builtins/dispatch.ll | 78 ++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/builtins/dispatch.ll b/builtins/dispatch.ll index 10ba05a8..e61292aa 100644 --- a/builtins/dispatch.ll +++ b/builtins/dispatch.ll @@ -48,23 +48,42 @@ declare void @abort() noreturn ;; corresponding to one of the Target::ISA enumerant values that gives the ;; most capable ISA that the curremt system can run. ;; -;; #ifdef _MSC_VER -;; extern void __stdcall __cpuid(int info[4], int infoType); -;; #else +;; Note: clang from LLVM 2.9 should be used if this is updated, for maximum +;; backwards compatibility for anyone building ispc with LLVM 2.9. +;; +;; #include +;; #include +;; ;; static void __cpuid(int info[4], int infoType) { ;; __asm__ __volatile__ ("cpuid" ;; : "=a" (info[0]), "=b" (info[1]), "=c" (info[2]), "=d" (info[3]) ;; : "0" (infoType)); ;; } -;; #endif +;; +;; /* Save %ebx in case it's the PIC register */ +;; static void __cpuid_count(int info[4], int level, int count) { +;; __asm__ __volatile__ ("xchg{l}\t{%%}ebx, %1\n\t" +;; "cpuid\n\t" +;; "xchg{l}\t{%%}ebx, %1\n\t" +;; : "=a" (info[0]), "=r" (info[1]), "=c" (info[2]), "=d" (info[3]) +;; : "0" (level), "2" (count)); +;; } ;; ;; int32_t __get_system_isa() { ;; int info[4]; ;; __cpuid(info, 1); +;; ;; /* NOTE: the values returned below must be the same as the ;; corresponding enumerant values in Target::ISA. */ -;; if ((info[2] & (1 << 28)) != 0) -;; return 2; // AVX +;; if ((info[2] & (1 << 28)) != 0) { +;; // AVX1 for sure. Do we have AVX2? +;; // Call cpuid with eax=7, ecx=0 +;; __cpuid_count(info, 7, 0); +;; if ((info[1] & (1 << 5)) != 0) +;; return 3; // AVX2 +;; else +;; return 2; // AVX1 +;; } ;; else if ((info[2] & (1 << 19)) != 0) ;; return 1; // SSE4 ;; else if ((info[3] & (1 << 26)) != 0) @@ -76,33 +95,42 @@ declare void @abort() noreturn %0 = type { i32, i32, i32, i32 } define i32 @__get_system_isa() nounwind ssp { - %1 = tail call %0 asm sideeffect "cpuid", "={ax},={bx},={cx},={dx},0,~{dirflag},~{fpsr},~{flags}"(i32 1) nounwind - %2 = extractvalue %0 %1, 2 - %3 = extractvalue %0 %1, 3 - %4 = and i32 %2, 268435456 - %5 = icmp eq i32 %4, 0 - br i1 %5, label %6, label %13 +entry: + %0 = tail call %0 asm sideeffect "cpuid", "={ax},={bx},={cx},={dx},0,~{dirflag},~{fpsr},~{flags}"(i32 1) nounwind + %asmresult9.i = extractvalue %0 %0, 2 + %asmresult10.i = extractvalue %0 %0, 3 + %and = and i32 %asmresult9.i, 268435456 + %cmp = icmp eq i32 %and, 0 + br i1 %cmp, label %if.else7, label %if.then -;