Add initial support for 'goto' statements.

ispc now supports goto, but only under uniform control flow--i.e.
it must be possible for the compiler to statically determine that
all program instances will follow the goto.  An error is issued at
compile time if a goto is used when this is not the case.
This commit is contained in:
Matt Pharr
2012-01-05 12:20:44 -08:00
parent 48e9d4af39
commit 78c6d3c02f
20 changed files with 408 additions and 15 deletions

View File

@@ -91,6 +91,7 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
ForStmt *fs;
ForeachStmt *fes;
ReturnStmt *rs;
LabeledStmt *ls;
StmtList *sl;
PrintStmt *ps;
AssertStmt *as;
@@ -131,9 +132,12 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
fes->stmts = (Stmt *)WalkAST(fes->stmts, preFunc, postFunc, data);
}
else if (dynamic_cast<BreakStmt *>(node) != NULL ||
dynamic_cast<ContinueStmt *>(node) != NULL) {
// nothing
dynamic_cast<ContinueStmt *>(node) != NULL ||
dynamic_cast<GotoStmt *>(node) != NULL) {
// nothing
}
else if ((ls = dynamic_cast<LabeledStmt *>(node)) != NULL)
ls->stmt = (Stmt *)WalkAST(ls->stmt, preFunc, postFunc, data);
else if ((rs = dynamic_cast<ReturnStmt *>(node)) != NULL)
rs->val = (Expr *)WalkAST(rs->val, preFunc, postFunc, data);
else if ((sl = dynamic_cast<StmtList *>(node)) != NULL) {
@@ -289,3 +293,4 @@ EstimateCost(ASTNode *root) {
WalkAST(root, lCostCallback, NULL, &cost);
return cost;
}

View File

@@ -376,7 +376,7 @@ namespace {
if (I.getType() == Type::getVoidTy(I.getContext()) || !I.hasOneUse() ||
isa<TerminatorInst>(I) || isa<CallInst>(I) || isa<PHINode>(I) ||
isa<LoadInst>(I) || isa<VAArgInst>(I) || isa<InsertElementInst>(I) ||
isa<InsertValueInst>(I) || isa<ExtractValueInst>(I))
isa<InsertValueInst>(I) || isa<ExtractValueInst>(I) || isa<SelectInst>(I))
// Don't inline a load across a store or other bad things!
return false;

45
ctx.cpp
View File

@@ -77,7 +77,7 @@ struct CFInfo {
bool IsIf() { return type == If; }
bool IsLoop() { return type == Loop; }
bool IsForeach() { return type == Foreach; }
bool IsVaryingType() { return !isUniform; }
bool IsVarying() { return !isUniform; }
bool IsUniform() { return isUniform; }
enum CFType { If, Loop, Foreach };
@@ -157,9 +157,10 @@ CFInfo::GetForeach(llvm::BasicBlock *breakTarget,
///////////////////////////////////////////////////////////////////////////
FunctionEmitContext::FunctionEmitContext(Function *func, Symbol *funSym,
llvm::Function *llvmFunction,
llvm::Function *lf,
SourcePos firstStmtPos) {
function = func;
llvmFunction = lf;
/* Create a new basic block to store all of the allocas */
allocaBlock = llvm::BasicBlock::Create(*g->ctx, "allocas", llvmFunction, 0);
@@ -762,7 +763,7 @@ int
FunctionEmitContext::VaryingCFDepth() const {
int sum = 0;
for (unsigned int i = 0; i < controlFlowInfo.size(); ++i)
if (controlFlowInfo[i]->IsVaryingType())
if (controlFlowInfo[i]->IsVarying())
++sum;
return sum;
}
@@ -777,6 +778,41 @@ FunctionEmitContext::InForeachLoop() const {
}
bool
FunctionEmitContext::initLabelBBlocks(ASTNode *node, void *data) {
LabeledStmt *ls = dynamic_cast<LabeledStmt *>(node);
if (ls == NULL)
return true;
FunctionEmitContext *ctx = (FunctionEmitContext *)data;
if (ctx->labelMap.find(ls->name) != ctx->labelMap.end())
Error(ls->pos, "Multiple labels named \"%s\" in function.",
ls->name.c_str());
else {
llvm::BasicBlock *bb = ctx->CreateBasicBlock(ls->name.c_str());
ctx->labelMap[ls->name] = bb;
}
return true;
}
void
FunctionEmitContext::InitializeLabelMap(Stmt *code) {
labelMap.erase(labelMap.begin(), labelMap.end());
WalkAST(code, initLabelBBlocks, NULL, this);
}
llvm::BasicBlock *
FunctionEmitContext::GetLabeledBasicBlock(const std::string &label) {
if (labelMap.find(label) != labelMap.end())
return labelMap[label];
else
return NULL;
}
void
FunctionEmitContext::CurrentLanesReturned(Expr *expr, bool doCoherenceCheck) {
const Type *returnType = function->GetReturnType();
@@ -920,8 +956,7 @@ FunctionEmitContext::GetStringPtr(const std::string &str) {
llvm::BasicBlock *
FunctionEmitContext::CreateBasicBlock(const char *name) {
llvm::Function *function = bblock->getParent();
return llvm::BasicBlock::Create(*g->ctx, name, function);
return llvm::BasicBlock::Create(*g->ctx, name, llvmFunction);
}

17
ctx.h
View File

@@ -39,6 +39,7 @@
#define ISPC_CTX_H 1
#include "ispc.h"
#include <map>
#include <llvm/InstrTypes.h>
#include <llvm/Instructions.h>
#include <llvm/Analysis/DIBuilder.h>
@@ -192,6 +193,15 @@ public:
bool InForeachLoop() const;
/** Step through the code and find label statements; create a basic
block for each one, so that subsequent calls to
GetLabeledBasicBlock() return the corresponding basic block. */
void InitializeLabelMap(Stmt *code);
/** If there is a label in the function with the given name, return the
new basic block that it starts. */
llvm::BasicBlock *GetLabeledBasicBlock(const std::string &label);
/** Called to generate code for 'return' statement; value is the
expression in the return statement (if non-NULL), and
doCoherenceCheck indicates whether instructions should be generated
@@ -446,6 +456,9 @@ private:
/** Pointer to the Function for which we're currently generating code. */
Function *function;
/** LLVM function representation for the current function. */
llvm::Function *llvmFunction;
/** The basic block into which we add any alloca instructions that need
to go at the very start of the function. */
llvm::BasicBlock *allocaBlock;
@@ -537,6 +550,10 @@ private:
tasks launched from the current function. */
llvm::Value *launchGroupHandlePtr;
std::map<std::string, llvm::BasicBlock *> labelMap;
static bool initLabelBBlocks(ASTNode *node, void *data);
llvm::Value *pointerVectorToVoidPointers(llvm::Value *value);
static void addGSMetadata(llvm::Value *inst, SourcePos pos);
bool ifsInLoopAllUniform() const;

View File

@@ -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
-----------------------------------------------------

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -290,8 +290,10 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function,
llvm::BasicBlock *bbAllOn = ctx->CreateBasicBlock("all_on");
llvm::BasicBlock *bbNotAll = ctx->CreateBasicBlock("not_all_on");
ctx->BranchInst(bbAllOn, bbNotAll, allOn);
// Set up basic blocks for goto targets
ctx->InitializeLabelMap(code);
ctx->BranchInst(bbAllOn, bbNotAll, allOn);
// all on: we've determined dynamically that the mask is all
// on. Set the current mask to "all on" explicitly so that
// codegen for this path can be improved with this knowledge in
@@ -322,12 +324,19 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function,
// above
ctx->SetCurrentBasicBlock(bbSomeOn);
ctx->SetFunctionMask(mask);
// Set up basic blocks for goto targets again; we want to have
// one set of them for gotos in the 'all on' case, and a
// distinct set for the 'mixed mask' case.
ctx->InitializeLabelMap(code);
code->EmitCode(ctx);
if (ctx->GetCurrentBasicBlock())
ctx->ReturnInst();
}
else
// Set up basic blocks for goto targets
ctx->InitializeLabelMap(code);
// No check, just emit the code
code->EmitCode(ctx);
}

3
ispc.h
View File

@@ -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,

View File

@@ -224,7 +224,7 @@ struct ForeachDimension {
%type <declSpecs> declaration_specifiers
%type <stringVal> string_constant
%type <constCharPtr> struct_or_union_name enum_identifier
%type <constCharPtr> struct_or_union_name enum_identifier goto_identifier
%type <intVal> int_constant soa_width_specifier
%type <foreachDimension> foreach_dimension_specifier
@@ -1262,7 +1262,11 @@ statement
;
labeled_statement
: TOKEN_CASE constant_expression ':' statement
: goto_identifier ':' statement
{
$$ = new LabeledStmt($1, $3, @1);
}
| TOKEN_CASE constant_expression ':' statement
{ UNIMPLEMENTED; }
| TOKEN_DEFAULT ':' statement
{ UNIMPLEMENTED; }
@@ -1433,9 +1437,13 @@ iteration_statement
}
;
goto_identifier
: TOKEN_IDENTIFIER { $$ = yylval.stringVal->c_str(); }
;
jump_statement
: TOKEN_GOTO TOKEN_IDENTIFIER ';'
{ UNIMPLEMENTED; }
: TOKEN_GOTO goto_identifier ';'
{ $$ = new GotoStmt($2, @1, @2); }
| TOKEN_CONTINUE ';'
{ $$ = new ContinueStmt(false, @1); }
| TOKEN_BREAK ';'

127
stmt.cpp
View File

@@ -494,6 +494,7 @@ lEmitIfStatements(FunctionEmitContext *ctx, Stmt *stmts, const char *trueOrFalse
ctx->EndScope();
}
void
IfStmt::EmitCode(FunctionEmitContext *ctx) const {
// First check all of the things that might happen due to errors
@@ -1915,6 +1916,132 @@ ReturnStmt::Print(int indent) const {
}
///////////////////////////////////////////////////////////////////////////
// GotoStmt
GotoStmt::GotoStmt(const char *l, SourcePos gotoPos, SourcePos ip)
: Stmt(gotoPos) {
label = l;
identifierPos = ip;
}
void
GotoStmt::EmitCode(FunctionEmitContext *ctx) const {
if (ctx->VaryingCFDepth() > 0) {
Error(pos, "\"goto\" statements are only legal under \"uniform\" "
"control flow.");
return;
}
if (ctx->InForeachLoop()) {
Error(pos, "\"goto\" statements are currently illegal inside "
"\"foreach\" loops.");
return;
}
llvm::BasicBlock *bb = ctx->GetLabeledBasicBlock(label);
if (bb == NULL) {
// TODO: use the string distance stuff to suggest alternatives if
// there are some with names close to the label name we have here..
Error(identifierPos, "No label named \"%s\" found in current function.",
label.c_str());
return;
}
ctx->BranchInst(bb);
ctx->SetCurrentBasicBlock(NULL);
}
void
GotoStmt::Print(int indent) const {
printf("%*cGoto label \"%s\"\n", indent, ' ', label.c_str());
}
Stmt *
GotoStmt::Optimize() {
return this;
}
Stmt *
GotoStmt::TypeCheck() {
return this;
}
int
GotoStmt::EstimateCost() const {
return COST_GOTO;
}
///////////////////////////////////////////////////////////////////////////
// LabeledStmt
LabeledStmt::LabeledStmt(const char *n, Stmt *s, SourcePos p)
: Stmt(p) {
name = n;
stmt = s;
}
void
LabeledStmt::EmitCode(FunctionEmitContext *ctx) const {
llvm::BasicBlock *bblock = ctx->GetLabeledBasicBlock(name);
assert(bblock != NULL);
// End the current basic block with a jump to our basic block and then
// set things up for emission to continue there. Note that the current
// basic block may validly be NULL going into this statement due to an
// earlier goto that NULLed it out; that doesn't stop us from
// re-establishing a current basic block starting at the label..
if (ctx->GetCurrentBasicBlock() != NULL)
ctx->BranchInst(bblock);
ctx->SetCurrentBasicBlock(bblock);
if (stmt != NULL)
stmt->EmitCode(ctx);
}
void
LabeledStmt::Print(int indent) const {
printf("%*cLabel \"%s\"\n", indent, ' ', name.c_str());
if (stmt != NULL)
stmt->Print(indent);
}
Stmt *
LabeledStmt::Optimize() {
return this;
}
Stmt *
LabeledStmt::TypeCheck() {
if (!isalpha(name[0]) || name[0] == '_') {
Error(pos, "Label must start with either alphabetic character or '_'.");
return NULL;
}
for (unsigned int i = 1; i < name.size(); ++i) {
if (!isalnum(name[i]) && name[i] != '_') {
Error(pos, "Character \"%c\" is illegal in labels.", name[i]);
return NULL;
}
}
return this;
}
int
LabeledStmt::EstimateCost() const {
return 0;
}
///////////////////////////////////////////////////////////////////////////
// StmtList

32
stmt.h
View File

@@ -282,6 +282,38 @@ public:
};
class GotoStmt : public Stmt {
public:
GotoStmt(const char *label, SourcePos gotoPos, SourcePos idPos);
void EmitCode(FunctionEmitContext *ctx) const;
void Print(int indent) const;
Stmt *Optimize();
Stmt *TypeCheck();
int EstimateCost() const;
std::string label;
SourcePos identifierPos;
};
class LabeledStmt : public Stmt {
public:
LabeledStmt(const char *label, Stmt *stmt, SourcePos p);
void EmitCode(FunctionEmitContext *ctx) const;
void Print(int indent) const;
Stmt *Optimize();
Stmt *TypeCheck();
int EstimateCost() const;
std::string name;
Stmt *stmt;
};
/** @brief Representation of a list of statements in the program.
*/
class StmtList : public Stmt {

17
tests/goto-1.ispc Normal file
View File

@@ -0,0 +1,17 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
float a = aFOO[programIndex];
float b = 0.; b = a;
RET[programIndex] = a+b;
goto skip;
RET[programIndex] = 0;
skip:
;
}
export void result(uniform float RET[]) {
RET[programIndex] = 2 + 2*programIndex;
}

18
tests/goto-2.ispc Normal file
View File

@@ -0,0 +1,18 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
float a = aFOO[programIndex];
float b = 0.; b = a;
RET[programIndex] = a+b;
if (all(a != 0))
goto skip;
RET[programIndex] = 0;
skip:
;
}
export void result(uniform float RET[]) {
RET[programIndex] = 2 + 2*programIndex;
}

18
tests/goto-3.ispc Normal file
View File

@@ -0,0 +1,18 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
float a = aFOO[programIndex];
float b = 0.; b = a;
RET[programIndex] = a+b;
if (all(a == 0))
goto skip;
RET[programIndex] = 0;
skip:
;
}
export void result(uniform float RET[]) {
RET[programIndex] = 0;
}

19
tests/goto-4.ispc Normal file
View File

@@ -0,0 +1,19 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
float a = aFOO[programIndex];
float b = 0.; b = a;
RET[programIndex] = 0;
encore:
++RET[programIndex];
if (any(a != 0)) {
a = max(a-1, 0);
goto encore;
}
}
export void result(uniform float RET[]) {
RET[programIndex] = programCount+1;
}

10
tests_errors/goto-1.ispc Normal file
View File

@@ -0,0 +1,10 @@
// Multiple labels named "label" in function
void func(int x) {
label:
;
label:
;
}

11
tests_errors/goto-2.ispc Normal file
View File

@@ -0,0 +1,11 @@
// "goto" statements are only legal under "uniform" control flow
void func(int x) {
if (x < 0)
goto label;
label:
;
}

11
tests_errors/goto-3.ispc Normal file
View File

@@ -0,0 +1,11 @@
// "goto" statements are only legal under "uniform" control flow
void func(int x) {
cif (x < 0)
goto label;
label:
;
}

10
tests_errors/goto-4.ispc Normal file
View File

@@ -0,0 +1,10 @@
// "goto" statements are only legal under "uniform" control flow
void func(int x) {
label:
for(int i =0 ;i<x;)
goto label;
}