10
ast.cpp
10
ast.cpp
@@ -98,6 +98,7 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
|||||||
StmtList *sl;
|
StmtList *sl;
|
||||||
PrintStmt *ps;
|
PrintStmt *ps;
|
||||||
AssertStmt *as;
|
AssertStmt *as;
|
||||||
|
DeleteStmt *dels;
|
||||||
|
|
||||||
if ((es = dynamic_cast<ExprStmt *>(node)) != NULL)
|
if ((es = dynamic_cast<ExprStmt *>(node)) != NULL)
|
||||||
es->expr = (Expr *)WalkAST(es->expr, preFunc, postFunc, data);
|
es->expr = (Expr *)WalkAST(es->expr, preFunc, postFunc, data);
|
||||||
@@ -160,6 +161,8 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
|||||||
ps->values = (Expr *)WalkAST(ps->values, preFunc, postFunc, data);
|
ps->values = (Expr *)WalkAST(ps->values, preFunc, postFunc, data);
|
||||||
else if ((as = dynamic_cast<AssertStmt *>(node)) != NULL)
|
else if ((as = dynamic_cast<AssertStmt *>(node)) != NULL)
|
||||||
as->expr = (Expr *)WalkAST(as->expr, preFunc, postFunc, data);
|
as->expr = (Expr *)WalkAST(as->expr, preFunc, postFunc, data);
|
||||||
|
else if ((dels = dynamic_cast<DeleteStmt *>(node)) != NULL)
|
||||||
|
dels->expr = (Expr *)WalkAST(dels->expr, preFunc, postFunc, data);
|
||||||
else
|
else
|
||||||
FATAL("Unhandled statement type in WalkAST()");
|
FATAL("Unhandled statement type in WalkAST()");
|
||||||
}
|
}
|
||||||
@@ -180,6 +183,7 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
|||||||
DereferenceExpr *dre;
|
DereferenceExpr *dre;
|
||||||
SizeOfExpr *soe;
|
SizeOfExpr *soe;
|
||||||
AddressOfExpr *aoe;
|
AddressOfExpr *aoe;
|
||||||
|
NewExpr *newe;
|
||||||
|
|
||||||
if ((ue = dynamic_cast<UnaryExpr *>(node)) != NULL)
|
if ((ue = dynamic_cast<UnaryExpr *>(node)) != NULL)
|
||||||
ue->expr = (Expr *)WalkAST(ue->expr, preFunc, postFunc, data);
|
ue->expr = (Expr *)WalkAST(ue->expr, preFunc, postFunc, data);
|
||||||
@@ -223,6 +227,12 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
|||||||
soe->expr = (Expr *)WalkAST(soe->expr, preFunc, postFunc, data);
|
soe->expr = (Expr *)WalkAST(soe->expr, preFunc, postFunc, data);
|
||||||
else if ((aoe = dynamic_cast<AddressOfExpr *>(node)) != NULL)
|
else if ((aoe = dynamic_cast<AddressOfExpr *>(node)) != NULL)
|
||||||
aoe->expr = (Expr *)WalkAST(aoe->expr, preFunc, postFunc, data);
|
aoe->expr = (Expr *)WalkAST(aoe->expr, preFunc, postFunc, data);
|
||||||
|
else if ((newe = dynamic_cast<NewExpr *>(node)) != NULL) {
|
||||||
|
newe->countExpr = (Expr *)WalkAST(newe->countExpr, preFunc,
|
||||||
|
postFunc, data);
|
||||||
|
newe->initExpr = (Expr *)WalkAST(newe->initExpr, preFunc,
|
||||||
|
postFunc, data);
|
||||||
|
}
|
||||||
else if (dynamic_cast<SymbolExpr *>(node) != NULL ||
|
else if (dynamic_cast<SymbolExpr *>(node) != NULL ||
|
||||||
dynamic_cast<ConstExpr *>(node) != NULL ||
|
dynamic_cast<ConstExpr *>(node) != NULL ||
|
||||||
dynamic_cast<FunctionSymbolExpr *>(node) != NULL ||
|
dynamic_cast<FunctionSymbolExpr *>(node) != NULL ||
|
||||||
|
|||||||
@@ -391,6 +391,8 @@ lSetInternalFunctions(llvm::Module *module) {
|
|||||||
"__count_trailing_zeros_i64",
|
"__count_trailing_zeros_i64",
|
||||||
"__count_leading_zeros_i32",
|
"__count_leading_zeros_i32",
|
||||||
"__count_leading_zeros_i64",
|
"__count_leading_zeros_i64",
|
||||||
|
"__delete_uniform",
|
||||||
|
"__delete_varying",
|
||||||
"__do_assert_uniform",
|
"__do_assert_uniform",
|
||||||
"__do_assert_varying",
|
"__do_assert_varying",
|
||||||
"__do_print",
|
"__do_print",
|
||||||
@@ -449,6 +451,9 @@ lSetInternalFunctions(llvm::Module *module) {
|
|||||||
"__min_varying_uint32",
|
"__min_varying_uint32",
|
||||||
"__min_varying_uint64",
|
"__min_varying_uint64",
|
||||||
"__movmsk",
|
"__movmsk",
|
||||||
|
"__new_uniform",
|
||||||
|
"__new_varying32",
|
||||||
|
"__new_varying64",
|
||||||
"__num_cores",
|
"__num_cores",
|
||||||
"__packed_load_active",
|
"__packed_load_active",
|
||||||
"__packed_store_active",
|
"__packed_store_active",
|
||||||
|
|||||||
@@ -1805,6 +1805,65 @@ ok:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; new/delete
|
||||||
|
|
||||||
|
declare i8 * @malloc(i64)
|
||||||
|
declare void @free(i8 *)
|
||||||
|
|
||||||
|
define i8 * @__new_uniform(i64 %size) {
|
||||||
|
%a = call i8 * @malloc(i64 %size)
|
||||||
|
ret i8 * %a
|
||||||
|
}
|
||||||
|
|
||||||
|
define <WIDTH x i64> @__new_varying32(<WIDTH x i32> %size, <WIDTH x MASK> %mask) {
|
||||||
|
%ret = alloca <WIDTH x i64>
|
||||||
|
store <WIDTH x i64> zeroinitializer, <WIDTH x i64> * %ret
|
||||||
|
%ret64 = bitcast <WIDTH x i64> * %ret to i64 *
|
||||||
|
|
||||||
|
per_lane(WIDTH, <WIDTH x MASK> %mask, `
|
||||||
|
%sz_LANE_ID = extractelement <WIDTH x i32> %size, i32 LANE
|
||||||
|
%sz64_LANE_ID = zext i32 %sz_LANE_ID to i64
|
||||||
|
%ptr_LANE_ID = call i8 * @malloc(i64 %sz64_LANE_ID)
|
||||||
|
%ptr_int_LANE_ID = ptrtoint i8 * %ptr_LANE_ID to i64
|
||||||
|
%store_LANE_ID = getelementptr i64 * %ret64, i32 LANE
|
||||||
|
store i64 %ptr_int_LANE_ID, i64 * %store_LANE_ID')
|
||||||
|
|
||||||
|
%r = load <WIDTH x i64> * %ret
|
||||||
|
ret <WIDTH x i64> %r
|
||||||
|
}
|
||||||
|
|
||||||
|
define <WIDTH x i64> @__new_varying64(<WIDTH x i64> %size, <WIDTH x MASK> %mask) {
|
||||||
|
%ret = alloca <WIDTH x i64>
|
||||||
|
store <WIDTH x i64> zeroinitializer, <WIDTH x i64> * %ret
|
||||||
|
%ret64 = bitcast <WIDTH x i64> * %ret to i64 *
|
||||||
|
|
||||||
|
per_lane(WIDTH, <WIDTH x MASK> %mask, `
|
||||||
|
%sz_LANE_ID = extractelement <WIDTH x i64> %size, i32 LANE
|
||||||
|
%ptr_LANE_ID = call i8 * @malloc(i64 %sz_LANE_ID)
|
||||||
|
%ptr_int_LANE_ID = ptrtoint i8 * %ptr_LANE_ID to i64
|
||||||
|
%store_LANE_ID = getelementptr i64 * %ret64, i32 LANE
|
||||||
|
store i64 %ptr_int_LANE_ID, i64 * %store_LANE_ID')
|
||||||
|
|
||||||
|
%r = load <WIDTH x i64> * %ret
|
||||||
|
ret <WIDTH x i64> %r
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @__delete_uniform(i8 * %ptr) {
|
||||||
|
call void @free(i8 * %ptr)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @__delete_varying(<WIDTH x i64> %ptr, <WIDTH x MASK> %mask) {
|
||||||
|
per_lane(WIDTH, <WIDTH x MASK> %mask, `
|
||||||
|
%iptr_LANE_ID = extractelement <WIDTH x i64> %ptr, i32 LANE
|
||||||
|
%ptr_LANE_ID = inttoptr i64 %iptr_LANE_ID to i8 *
|
||||||
|
call void @free(i8 * %ptr_LANE_ID)
|
||||||
|
')
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; read hw clock
|
;; read hw clock
|
||||||
|
|
||||||
|
|||||||
2
ctx.cpp
2
ctx.cpp
@@ -2923,7 +2923,7 @@ FunctionEmitContext::SyncInst() {
|
|||||||
|
|
||||||
|
|
||||||
/** When we gathering from or scattering to a varying atomic type, we need
|
/** When we gathering from or scattering to a varying atomic type, we need
|
||||||
to add an appropraite offset to the final address for each lane right
|
to add an appropriate offset to the final address for each lane right
|
||||||
before we use it. Given a varying pointer we're about to use and its
|
before we use it. Given a varying pointer we're about to use and its
|
||||||
type, this function determines whether these offsets are needed and
|
type, this function determines whether these offsets are needed and
|
||||||
returns an updated pointer that incorporates these offsets if needed.
|
returns an updated pointer that incorporates these offsets if needed.
|
||||||
|
|||||||
115
docs/ispc.rst
115
docs/ispc.rst
@@ -96,6 +96,9 @@ Contents:
|
|||||||
|
|
||||||
+ `Declarations and Initializers`_
|
+ `Declarations and Initializers`_
|
||||||
+ `Expressions`_
|
+ `Expressions`_
|
||||||
|
|
||||||
|
* `Dynamic Memory Allocation`_
|
||||||
|
|
||||||
+ `Control Flow`_
|
+ `Control Flow`_
|
||||||
|
|
||||||
* `Conditional Statements: "if"`_
|
* `Conditional Statements: "if"`_
|
||||||
@@ -1162,6 +1165,7 @@ in C:
|
|||||||
* The ``inline`` qualifier to indicate that a function should be inlined
|
* The ``inline`` qualifier to indicate that a function should be inlined
|
||||||
* Function overloading by parameter type
|
* Function overloading by parameter type
|
||||||
* Hexadecimal floating-point constants
|
* Hexadecimal floating-point constants
|
||||||
|
* Dynamic memory allocation with ``new`` and ``delete``.
|
||||||
|
|
||||||
``ispc`` also adds a number of new features that aren't in C89, C99, or
|
``ispc`` also adds a number of new features that aren't in C89, C99, or
|
||||||
C++:
|
C++:
|
||||||
@@ -1966,18 +1970,123 @@ operator also work as expected.
|
|||||||
fp->b = 1;
|
fp->b = 1;
|
||||||
|
|
||||||
|
|
||||||
|
Dynamic Memory Allocation
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
``ispc`` programs can dynamically allocate (and free) memory, using syntax
|
||||||
|
based on C++'s ``new`` and ``delete`` operators:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
int count = ...;
|
||||||
|
int *ptr = new uniform int[count];
|
||||||
|
// use ptr...
|
||||||
|
delete[] ptr;
|
||||||
|
|
||||||
|
In the above code, each program instance allocates its own ``count`-sized
|
||||||
|
array of ``uniform int`` values, uses that memory, and then deallocates
|
||||||
|
that memory. Uses of ``new`` and ``delete`` in ``ispc`` programs are
|
||||||
|
serviced by corresponding calls the system C library's ``malloc()`` and
|
||||||
|
``free()`` functions.
|
||||||
|
|
||||||
|
After a pointer has been deleted, it is illegal to access the memory it
|
||||||
|
points to. However, note that deletion happens on a per-program-instance
|
||||||
|
basis. In other words, consider the following code:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
int *ptr = new uniform int[count];
|
||||||
|
// use ptr
|
||||||
|
if (count > 1000)
|
||||||
|
delete[] ptr;
|
||||||
|
// ...
|
||||||
|
|
||||||
|
Here, the program instances where ``count`` is greater than 1000 have
|
||||||
|
deleted the dynamically allocated memory pointed to by ``ptr``, but the
|
||||||
|
other program instances have not. As such, it's illegal for the former set
|
||||||
|
of program instances to access ``*ptr``, but it's perfectly fine for the
|
||||||
|
latter set to continue to use the memory ``ptr`` points to. Note that it
|
||||||
|
is illegal to delete a pointer value returned by ``new`` more than one
|
||||||
|
time.
|
||||||
|
|
||||||
|
Sometimes, it's useful to be able to do a single allocation for the entire
|
||||||
|
gang of program instances. A ``new`` statement can be qualified with
|
||||||
|
``uniform`` to indicate a single memory allocation:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
float * uniform ptr = uniform new float[10];
|
||||||
|
|
||||||
|
While a regular call to ``new`` returns a ``varying`` pointer (i.e. a
|
||||||
|
distinct pointer to separately-allocated memory for each program instance),
|
||||||
|
a ``uniform new`` performs a single allocation and returns a ``uniform``
|
||||||
|
pointer.
|
||||||
|
|
||||||
|
When using ``uniform new``, it's important to be aware of a subtlety; if
|
||||||
|
the returned pointer is stored in a varying pointer variable (as may be
|
||||||
|
appropriate and useful for the particular program being written), then the
|
||||||
|
varying pointer may inadvertently be passed to a subsequent ``delete``
|
||||||
|
statement, which is an error: effectively
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
float *ptr = uniform new float[10];
|
||||||
|
// use ptr...
|
||||||
|
delete ptr; // ERROR: varying pointer is deleted
|
||||||
|
|
||||||
|
In this case, ``ptr`` will be deleted multiple times, once for each
|
||||||
|
executing program instance, which is an error (unless it happens that only
|
||||||
|
a single program instance is active in the above code.)
|
||||||
|
|
||||||
|
When using ``new`` statements, it's important to make an appropriate choice
|
||||||
|
of ``uniform`` or ``varying`` (as always, the default), for both the
|
||||||
|
``new`` operator itself as well as the type of data being allocated, based
|
||||||
|
on the program's needs. Consider the following four memory allocations:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
uniform float * uniform p1 = uniform new uniform float[10];
|
||||||
|
float * uniform p2 = uniform new float[10];
|
||||||
|
uniform float * p3 = new uniform float[10];
|
||||||
|
float * p4 = new float[10];
|
||||||
|
|
||||||
|
Assuming that a ``float`` is 4 bytes in memory and if the gang size is 8
|
||||||
|
program instances, then the first allocation represents a single allocation
|
||||||
|
of 40 bytes, the second is a single allocation of 8*4*10 = 320 bytes, the
|
||||||
|
third is 8 allocations of 40 bytes, and the last performs 8 allocations of
|
||||||
|
80 bytes each.
|
||||||
|
|
||||||
|
Note in particular that varying allocations of varying data types are rarely
|
||||||
|
desirable in practice. In that case, each program instance is performing a
|
||||||
|
separate allocation of ``varying float`` memory. In this case, it's likely
|
||||||
|
that the program instances will only access a single element of each
|
||||||
|
``varying float``, which is wasteful.
|
||||||
|
|
||||||
|
Although ``ispc`` doesn't support constructors or destructors like C++, it
|
||||||
|
is possible to provide initializer values with ``new`` statements:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
struct Point { float x, y, z; };
|
||||||
|
Point *pptr = new Point(10, 20, 30);
|
||||||
|
|
||||||
|
Here for example, the "x" element of the returned ``Point`` is initialized
|
||||||
|
to have the value 10 and so forth. In general, the rules for how
|
||||||
|
initializer values provided in ``new`` statements are used to initialize
|
||||||
|
complex data types follow the same rules as initializers for variables
|
||||||
|
described in `Declarations and Initializers`_.
|
||||||
|
|
||||||
Control Flow
|
Control Flow
|
||||||
------------
|
------------
|
||||||
|
|
||||||
``ispc`` supports most of C's control flow constructs, including ``if``,
|
``ispc`` supports most of C's control flow constructs, including ``if``,
|
||||||
``for``, ``while``, ``do``. It also supports variants of C's control flow
|
``switch``, ``for``, ``while``, ``do``. It has limited support for
|
||||||
|
``goto``, detailed below. It also supports variants of C's control flow
|
||||||
constructs that provide hints about the expected runtime coherence of the
|
constructs that provide hints about the expected runtime coherence of the
|
||||||
control flow at that statement. It also provides parallel looping
|
control flow at that statement. It also provides parallel looping
|
||||||
constructs, ``foreach`` and ``foreach_tiled``, all of which will be
|
constructs, ``foreach`` and ``foreach_tiled``, all of which will be
|
||||||
detailed in this section.
|
detailed in this section.
|
||||||
|
|
||||||
``ispc`` does not currently support ``switch`` statements or ``goto``.
|
|
||||||
|
|
||||||
Conditional Statements: "if"
|
Conditional Statements: "if"
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
|||||||
355
expr.cpp
355
expr.cpp
@@ -504,6 +504,153 @@ TypeConvertExpr(Expr *expr, const Type *toType, const char *errorMsgBase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
PossiblyResolveFunctionOverloads(Expr *expr, const Type *type) {
|
||||||
|
FunctionSymbolExpr *fse = NULL;
|
||||||
|
const FunctionType *funcType = NULL;
|
||||||
|
if (dynamic_cast<const PointerType *>(type) != NULL &&
|
||||||
|
(funcType = dynamic_cast<const FunctionType *>(type->GetBaseType())) &&
|
||||||
|
(fse = dynamic_cast<FunctionSymbolExpr *>(expr)) != NULL) {
|
||||||
|
// We're initializing a function pointer with a function symbol,
|
||||||
|
// which in turn may represent an overloaded function. So we need
|
||||||
|
// to try to resolve the overload based on the type of the symbol
|
||||||
|
// we're initializing here.
|
||||||
|
std::vector<const Type *> paramTypes;
|
||||||
|
for (int i = 0; i < funcType->GetNumParameters(); ++i)
|
||||||
|
paramTypes.push_back(funcType->GetParameterType(i));
|
||||||
|
|
||||||
|
if (fse->ResolveOverloads(expr->pos, paramTypes) == false)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Utility routine that emits code to initialize a symbol given an
|
||||||
|
initializer expression.
|
||||||
|
|
||||||
|
@param lvalue Memory location of storage for the symbol's data
|
||||||
|
@param symName Name of symbol (used in error messages)
|
||||||
|
@param symType Type of variable being initialized
|
||||||
|
@param initExpr Expression for the initializer
|
||||||
|
@param ctx FunctionEmitContext to use for generating instructions
|
||||||
|
@param pos Source file position of the variable being initialized
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
InitSymbol(llvm::Value *lvalue, const Type *symType, Expr *initExpr,
|
||||||
|
FunctionEmitContext *ctx, SourcePos pos) {
|
||||||
|
if (initExpr == NULL)
|
||||||
|
// leave it uninitialized
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If the initializer is a straight up expression that isn't an
|
||||||
|
// ExprList, then we'll see if we can type convert it to the type of
|
||||||
|
// the variable.
|
||||||
|
if (dynamic_cast<ExprList *>(initExpr) == NULL) {
|
||||||
|
if (PossiblyResolveFunctionOverloads(initExpr, symType) == false)
|
||||||
|
return;
|
||||||
|
initExpr = TypeConvertExpr(initExpr, symType, "initializer");
|
||||||
|
|
||||||
|
if (initExpr != NULL) {
|
||||||
|
llvm::Value *initializerValue = initExpr->GetValue(ctx);
|
||||||
|
if (initializerValue != NULL)
|
||||||
|
// Bingo; store the value in the variable's storage
|
||||||
|
ctx->StoreInst(initializerValue, lvalue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomic types and enums can't be initialized with { ... } initializer
|
||||||
|
// expressions, so print an error and return if that's what we've got
|
||||||
|
// here..
|
||||||
|
if (dynamic_cast<const AtomicType *>(symType) != NULL ||
|
||||||
|
dynamic_cast<const EnumType *>(symType) != NULL ||
|
||||||
|
dynamic_cast<const PointerType *>(symType) != NULL) {
|
||||||
|
ExprList *elist = dynamic_cast<ExprList *>(initExpr);
|
||||||
|
if (elist != NULL) {
|
||||||
|
if (elist->exprs.size() == 1)
|
||||||
|
InitSymbol(lvalue, symType, elist->exprs[0], ctx, pos);
|
||||||
|
else
|
||||||
|
Error(initExpr->pos, "Expression list initializers can't be used "
|
||||||
|
"with type \"%s\".", symType->GetString().c_str());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReferenceType *rt = dynamic_cast<const ReferenceType *>(symType);
|
||||||
|
if (rt) {
|
||||||
|
if (!Type::Equal(initExpr->GetType(), rt)) {
|
||||||
|
Error(initExpr->pos, "Initializer for reference type \"%s\" must have same "
|
||||||
|
"reference type itself. \"%s\" is incompatible.",
|
||||||
|
rt->GetString().c_str(), initExpr->GetType()->GetString().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *initializerValue = initExpr->GetValue(ctx);
|
||||||
|
if (initializerValue)
|
||||||
|
ctx->StoreInst(initializerValue, lvalue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are two cases for initializing structs, arrays and vectors;
|
||||||
|
// either a single initializer may be provided (float foo[3] = 0;), in
|
||||||
|
// which case all of the elements are initialized to the given value,
|
||||||
|
// or an initializer list may be provided (float foo[3] = { 1,2,3 }),
|
||||||
|
// in which case the elements are initialized with the corresponding
|
||||||
|
// values.
|
||||||
|
const CollectionType *collectionType =
|
||||||
|
dynamic_cast<const CollectionType *>(symType);
|
||||||
|
if (collectionType != NULL) {
|
||||||
|
std::string name;
|
||||||
|
if (dynamic_cast<const StructType *>(symType) != NULL)
|
||||||
|
name = "struct";
|
||||||
|
else if (dynamic_cast<const ArrayType *>(symType) != NULL)
|
||||||
|
name = "array";
|
||||||
|
else if (dynamic_cast<const VectorType *>(symType) != NULL)
|
||||||
|
name = "vector";
|
||||||
|
else
|
||||||
|
FATAL("Unexpected CollectionType in InitSymbol()");
|
||||||
|
|
||||||
|
ExprList *exprList = dynamic_cast<ExprList *>(initExpr);
|
||||||
|
if (exprList != NULL) {
|
||||||
|
// The { ... } case; make sure we have the same number of
|
||||||
|
// expressions in the ExprList as we have struct members
|
||||||
|
int nInits = exprList->exprs.size();
|
||||||
|
if (nInits != collectionType->GetElementCount()) {
|
||||||
|
Error(initExpr->pos, "Initializer for %s type \"%s\" requires "
|
||||||
|
"%d values; %d provided.", name.c_str(),
|
||||||
|
symType->GetString().c_str(),
|
||||||
|
collectionType->GetElementCount(), nInits);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize each element with the corresponding value from
|
||||||
|
// the ExprList
|
||||||
|
for (int i = 0; i < nInits; ++i) {
|
||||||
|
llvm::Value *ep;
|
||||||
|
if (dynamic_cast<const StructType *>(symType) != NULL)
|
||||||
|
ep = ctx->AddElementOffset(lvalue, i, NULL, "element");
|
||||||
|
else
|
||||||
|
ep = ctx->GetElementPtrInst(lvalue, LLVMInt32(0), LLVMInt32(i),
|
||||||
|
PointerType::GetUniform(collectionType->GetElementType(i)),
|
||||||
|
"gep");
|
||||||
|
|
||||||
|
InitSymbol(ep, collectionType->GetElementType(i),
|
||||||
|
exprList->exprs[i], ctx, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Error(initExpr->pos, "Can't assign type \"%s\" to \"%s\".",
|
||||||
|
initExpr->GetType()->GetString().c_str(),
|
||||||
|
collectionType->GetString().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FATAL("Unexpected Type in InitSymbol()");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/** Given an atomic or vector type, this returns a boolean type with the
|
/** Given an atomic or vector type, this returns a boolean type with the
|
||||||
@@ -6527,3 +6674,211 @@ NullPointerExpr::EstimateCost() const {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// NewExpr
|
||||||
|
|
||||||
|
NewExpr::NewExpr(int typeQual, const Type *t, Expr *init, Expr *count,
|
||||||
|
SourcePos tqPos, SourcePos p)
|
||||||
|
: Expr(p) {
|
||||||
|
allocType = t;
|
||||||
|
if (allocType != NULL && allocType->HasUnboundVariability())
|
||||||
|
allocType = allocType->ResolveUnboundVariability(Type::Varying);
|
||||||
|
|
||||||
|
initExpr = init;
|
||||||
|
countExpr = count;
|
||||||
|
|
||||||
|
/* (The below cases actually should be impossible, since the parser
|
||||||
|
doesn't allow more than a single type qualifier before a "new".) */
|
||||||
|
if ((typeQual & ~(TYPEQUAL_UNIFORM | TYPEQUAL_VARYING)) != 0) {
|
||||||
|
Error(tqPos, "Illegal type qualifiers in \"new\" expression (only "
|
||||||
|
"\"uniform\" and \"varying\" are allowed.");
|
||||||
|
isVarying = false;
|
||||||
|
}
|
||||||
|
else if ((typeQual & TYPEQUAL_UNIFORM) != 0 &&
|
||||||
|
(typeQual & TYPEQUAL_VARYING) != 0) {
|
||||||
|
Error(tqPos, "Illegal to provide both \"uniform\" and \"varying\" "
|
||||||
|
"qualifiers to \"new\" expression.");
|
||||||
|
isVarying = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// If no type qualifier is given before the 'new', treat it as a
|
||||||
|
// varying new.
|
||||||
|
isVarying = (typeQual == 0) || (typeQual & TYPEQUAL_VARYING);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
llvm::Value *
|
||||||
|
NewExpr::GetValue(FunctionEmitContext *ctx) const {
|
||||||
|
bool do32Bit = (g->target.is32Bit || g->opt.force32BitAddressing);
|
||||||
|
|
||||||
|
// Determine how many elements we need to allocate. Note that this
|
||||||
|
// will be a varying value if this is a varying new.
|
||||||
|
llvm::Value *countValue;
|
||||||
|
if (countExpr != NULL) {
|
||||||
|
countValue = countExpr->GetValue(ctx);
|
||||||
|
if (countValue == NULL) {
|
||||||
|
Assert(m->errorCount > 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isVarying) {
|
||||||
|
if (do32Bit) countValue = LLVMInt32Vector(1);
|
||||||
|
else countValue = LLVMInt64Vector(1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (do32Bit) countValue = LLVMInt32(1);
|
||||||
|
else countValue = LLVMInt64(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the total amount of memory to allocate, allocSize, as the
|
||||||
|
// product of the number of elements to allocate and the size of a
|
||||||
|
// single element.
|
||||||
|
llvm::Value *eltSize = g->target.SizeOf(allocType->LLVMType(g->ctx),
|
||||||
|
ctx->GetCurrentBasicBlock());
|
||||||
|
if (isVarying)
|
||||||
|
eltSize = ctx->SmearUniform(eltSize, "smear_size");
|
||||||
|
llvm::Value *allocSize = ctx->BinaryOperator(llvm::Instruction::Mul, countValue,
|
||||||
|
eltSize, "alloc_size");
|
||||||
|
|
||||||
|
// Determine which allocation builtin function to call: uniform or
|
||||||
|
// varying, and taking 32-bit or 64-bit allocation counts.
|
||||||
|
llvm::Function *func;
|
||||||
|
if (isVarying) {
|
||||||
|
if (do32Bit)
|
||||||
|
func = m->module->getFunction("__new_varying32");
|
||||||
|
else
|
||||||
|
func = m->module->getFunction("__new_varying64");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (allocSize->getType() != LLVMTypes::Int64Type)
|
||||||
|
allocSize = ctx->SExtInst(allocSize, LLVMTypes::Int64Type,
|
||||||
|
"alloc_size64");
|
||||||
|
func = m->module->getFunction("__new_uniform");
|
||||||
|
}
|
||||||
|
Assert(func != NULL);
|
||||||
|
|
||||||
|
// Make the call for the the actual allocation.
|
||||||
|
llvm::Value *ptrValue = ctx->CallInst(func, NULL, allocSize, "new");
|
||||||
|
|
||||||
|
// Now handle initializers and returning the right type for the result.
|
||||||
|
const Type *retType = GetType();
|
||||||
|
if (retType == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (isVarying) {
|
||||||
|
if (g->target.is32Bit)
|
||||||
|
// Convert i64 vector values to i32 if we are compiling to a
|
||||||
|
// 32-bit target.
|
||||||
|
ptrValue = ctx->TruncInst(ptrValue, LLVMTypes::VoidPointerVectorType,
|
||||||
|
"ptr_to_32bit");
|
||||||
|
|
||||||
|
if (initExpr != NULL) {
|
||||||
|
// If we have an initializer expression, emit code that checks
|
||||||
|
// to see if each lane is active and if so, runs the code to do
|
||||||
|
// the initialization. Note that we're we're taking advantage
|
||||||
|
// of the fact that the __new_varying*() functions are
|
||||||
|
// implemented to return NULL for program instances that aren't
|
||||||
|
// executing; more generally, we should be using the current
|
||||||
|
// execution mask for this...
|
||||||
|
for (int i = 0; i < g->target.vectorWidth; ++i) {
|
||||||
|
llvm::BasicBlock *bbInit = ctx->CreateBasicBlock("init_ptr");
|
||||||
|
llvm::BasicBlock *bbSkip = ctx->CreateBasicBlock("skip_init");
|
||||||
|
llvm::Value *p = ctx->ExtractInst(ptrValue, i);
|
||||||
|
llvm::Value *nullValue = g->target.is32Bit ? LLVMInt32(0) :
|
||||||
|
LLVMInt64(0);
|
||||||
|
// Is the pointer for the current lane non-zero?
|
||||||
|
llvm::Value *nonNull = ctx->CmpInst(llvm::Instruction::ICmp,
|
||||||
|
llvm::CmpInst::ICMP_NE,
|
||||||
|
p, nullValue, "non_null");
|
||||||
|
ctx->BranchInst(bbInit, bbSkip, nonNull);
|
||||||
|
|
||||||
|
// Initialize the memory pointed to by the pointer for the
|
||||||
|
// current lane.
|
||||||
|
ctx->SetCurrentBasicBlock(bbInit);
|
||||||
|
LLVM_TYPE_CONST llvm::Type *ptrType =
|
||||||
|
retType->GetAsUniformType()->LLVMType(g->ctx);
|
||||||
|
llvm::Value *ptr = ctx->IntToPtrInst(p, ptrType);
|
||||||
|
InitSymbol(ptr, allocType, initExpr, ctx, pos);
|
||||||
|
ctx->BranchInst(bbSkip);
|
||||||
|
|
||||||
|
ctx->SetCurrentBasicBlock(bbSkip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptrValue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// For uniform news, we just need to cast the void * to be a
|
||||||
|
// pointer of the return type and to run the code for initializers,
|
||||||
|
// if present.
|
||||||
|
LLVM_TYPE_CONST llvm::Type *ptrType = retType->LLVMType(g->ctx);
|
||||||
|
ptrValue = ctx->BitCastInst(ptrValue, ptrType, "cast_new_ptr");
|
||||||
|
|
||||||
|
if (initExpr != NULL)
|
||||||
|
InitSymbol(ptrValue, allocType, initExpr, ctx, pos);
|
||||||
|
|
||||||
|
return ptrValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Type *
|
||||||
|
NewExpr::GetType() const {
|
||||||
|
if (allocType == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return isVarying ? PointerType::GetVarying(allocType) :
|
||||||
|
PointerType::GetUniform(allocType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Expr *
|
||||||
|
NewExpr::TypeCheck() {
|
||||||
|
// Here we only need to make sure that if we have an expression giving
|
||||||
|
// a number of elements to allocate that it can be converted to an
|
||||||
|
// integer of the appropriate variability.
|
||||||
|
if (countExpr == NULL)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
const Type *countType;
|
||||||
|
if ((countType = countExpr->GetType()) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (isVarying == false && countType->IsVaryingType()) {
|
||||||
|
Error(pos, "Illegal to provide \"varying\" allocation count with "
|
||||||
|
"\"uniform new\" expression.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out the type that the allocation count should be
|
||||||
|
const Type *t = (g->target.is32Bit || g->opt.force32BitAddressing) ?
|
||||||
|
AtomicType::UniformUInt32 : AtomicType::UniformUInt64;
|
||||||
|
if (isVarying)
|
||||||
|
t = t->GetAsVaryingType();
|
||||||
|
|
||||||
|
countExpr = TypeConvertExpr(countExpr, t, "item count");
|
||||||
|
if (countExpr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Expr *
|
||||||
|
NewExpr::Optimize() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
NewExpr::Print() const {
|
||||||
|
printf("new (%s)", allocType ? allocType->GetString().c_str() : "NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
NewExpr::EstimateCost() const {
|
||||||
|
return COST_NEW;
|
||||||
|
}
|
||||||
|
|||||||
48
expr.h
48
expr.h
@@ -685,6 +685,38 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** An expression representing a "new" expression, used for dynamically
|
||||||
|
allocating memory.
|
||||||
|
*/
|
||||||
|
class NewExpr : public Expr {
|
||||||
|
public:
|
||||||
|
NewExpr(int typeQual, const Type *type, Expr *initializer, Expr *count,
|
||||||
|
SourcePos tqPos, SourcePos p);
|
||||||
|
|
||||||
|
llvm::Value *GetValue(FunctionEmitContext *ctx) const;
|
||||||
|
const Type *GetType() const;
|
||||||
|
Expr *TypeCheck();
|
||||||
|
Expr *Optimize();
|
||||||
|
void Print() const;
|
||||||
|
int EstimateCost() const;
|
||||||
|
|
||||||
|
/** Type of object to allocate storage for. */
|
||||||
|
const Type *allocType;
|
||||||
|
/** Expression giving the number of elements to allocate, when the
|
||||||
|
"new Foo[expr]" form is used. This may be NULL, in which case a
|
||||||
|
single element of the given type will be allocated. */
|
||||||
|
Expr *countExpr;
|
||||||
|
/** Optional initializer expression used to initialize the allocated
|
||||||
|
memory. */
|
||||||
|
Expr *initExpr;
|
||||||
|
/** Indicates whether this is a "varying new" or "uniform new"
|
||||||
|
(i.e. whether a separate allocation is performed per program
|
||||||
|
instance, or whether a single allocation is performed for the
|
||||||
|
entire gang of program instances.) */
|
||||||
|
bool isVarying;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/** This function indicates whether it's legal to convert from fromType to
|
/** This function indicates whether it's legal to convert from fromType to
|
||||||
toType. If the optional errorMsgBase and source position parameters
|
toType. If the optional errorMsgBase and source position parameters
|
||||||
are provided, then an error message is issued if the type conversion
|
are provided, then an error message is issued if the type conversion
|
||||||
@@ -703,4 +735,20 @@ bool CanConvertTypes(const Type *fromType, const Type *toType,
|
|||||||
*/
|
*/
|
||||||
Expr *TypeConvertExpr(Expr *expr, const Type *toType, const char *errorMsgBase);
|
Expr *TypeConvertExpr(Expr *expr, const Type *toType, const char *errorMsgBase);
|
||||||
|
|
||||||
|
/** Utility routine that emits code to initialize a symbol given an
|
||||||
|
initializer expression.
|
||||||
|
|
||||||
|
@param lvalue Memory location of storage for the symbol's data
|
||||||
|
@param symName Name of symbol (used in error messages)
|
||||||
|
@param symType Type of variable being initialized
|
||||||
|
@param initExpr Expression for the initializer
|
||||||
|
@param ctx FunctionEmitContext to use for generating instructions
|
||||||
|
@param pos Source file position of the variable being initialized
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
InitSymbol(llvm::Value *lvalue, const Type *symType, Expr *initExpr,
|
||||||
|
FunctionEmitContext *ctx, SourcePos pos);
|
||||||
|
|
||||||
|
bool PossiblyResolveFunctionOverloads(Expr *expr, const Type *type);
|
||||||
|
|
||||||
#endif // ISPC_EXPR_H
|
#endif // ISPC_EXPR_H
|
||||||
|
|||||||
2
ispc.h
2
ispc.h
@@ -418,6 +418,7 @@ enum {
|
|||||||
COST_ASSIGN = 1,
|
COST_ASSIGN = 1,
|
||||||
COST_COHERENT_BREAK_CONTINE = 4,
|
COST_COHERENT_BREAK_CONTINE = 4,
|
||||||
COST_COMPLEX_ARITH_OP = 4,
|
COST_COMPLEX_ARITH_OP = 4,
|
||||||
|
COST_DELETE = 32,
|
||||||
COST_DEREF = 4,
|
COST_DEREF = 4,
|
||||||
COST_FUNCALL = 4,
|
COST_FUNCALL = 4,
|
||||||
COST_FUNPTR_UNIFORM = 12,
|
COST_FUNPTR_UNIFORM = 12,
|
||||||
@@ -425,6 +426,7 @@ enum {
|
|||||||
COST_GATHER = 8,
|
COST_GATHER = 8,
|
||||||
COST_GOTO = 4,
|
COST_GOTO = 4,
|
||||||
COST_LOAD = 2,
|
COST_LOAD = 2,
|
||||||
|
COST_NEW = 32,
|
||||||
COST_REGULAR_BREAK_CONTINUE = 2,
|
COST_REGULAR_BREAK_CONTINUE = 2,
|
||||||
COST_RETURN = 4,
|
COST_RETURN = 4,
|
||||||
COST_SELECT = 4,
|
COST_SELECT = 4,
|
||||||
|
|||||||
3
lex.ll
3
lex.ll
@@ -93,6 +93,8 @@ continue { return TOKEN_CONTINUE; }
|
|||||||
creturn { return TOKEN_CRETURN; }
|
creturn { return TOKEN_CRETURN; }
|
||||||
default { return TOKEN_DEFAULT; }
|
default { return TOKEN_DEFAULT; }
|
||||||
do { return TOKEN_DO; }
|
do { return TOKEN_DO; }
|
||||||
|
delete { return TOKEN_DELETE; }
|
||||||
|
delete\[\] { return TOKEN_DELETE; }
|
||||||
double { return TOKEN_DOUBLE; }
|
double { return TOKEN_DOUBLE; }
|
||||||
else { return TOKEN_ELSE; }
|
else { return TOKEN_ELSE; }
|
||||||
enum { return TOKEN_ENUM; }
|
enum { return TOKEN_ENUM; }
|
||||||
@@ -112,6 +114,7 @@ int16 { return TOKEN_INT16; }
|
|||||||
int32 { return TOKEN_INT; }
|
int32 { return TOKEN_INT; }
|
||||||
int64 { return TOKEN_INT64; }
|
int64 { return TOKEN_INT64; }
|
||||||
launch { return TOKEN_LAUNCH; }
|
launch { return TOKEN_LAUNCH; }
|
||||||
|
new { return TOKEN_NEW; }
|
||||||
NULL { return TOKEN_NULL; }
|
NULL { return TOKEN_NULL; }
|
||||||
print { return TOKEN_PRINT; }
|
print { return TOKEN_PRINT; }
|
||||||
reference { Error(*yylloc, "\"reference\" qualifier is no longer supported; "
|
reference { Error(*yylloc, "\"reference\" qualifier is no longer supported; "
|
||||||
|
|||||||
63
parse.yy
63
parse.yy
@@ -106,13 +106,14 @@ static void lFinalizeEnumeratorSymbols(std::vector<Symbol *> &enums,
|
|||||||
const EnumType *enumType);
|
const EnumType *enumType);
|
||||||
|
|
||||||
static const char *lBuiltinTokens[] = {
|
static const char *lBuiltinTokens[] = {
|
||||||
"assert", "bool", "break", "case", "cbreak", "ccontinue", "cdo", "cfor",
|
"assert", "bool", "break", "case", "cbreak", "ccontinue", "cdo",
|
||||||
"cif", "cwhile", "const", "continue", "creturn", "default", "do", "double",
|
"cfor", "cif", "cwhile", "const", "continue", "creturn", "default",
|
||||||
"else", "enum", "export", "extern", "false", "float", "for", "foreach",
|
"do", "delete", "double", "else", "enum", "export", "extern", "false",
|
||||||
"foreach_tiled", "goto", "if", "inline", "int", "int8", "int16",
|
"float", "for", "foreach", "foreach_tiled", "goto", "if", "inline",
|
||||||
"int32", "int64", "launch", "NULL", "print", "return", "signed", "sizeof",
|
"int", "int8", "int16", "int32", "int64", "launch", "new", "NULL",
|
||||||
"static", "struct", "switch", "sync", "task", "true", "typedef", "uniform",
|
"print", "return", "signed", "sizeof", "static", "struct", "switch",
|
||||||
"unsigned", "varying", "void", "while", NULL
|
"sync", "task", "true", "typedef", "uniform", "unsigned", "varying",
|
||||||
|
"void", "while", NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *lParamListTokens[] = {
|
static const char *lParamListTokens[] = {
|
||||||
@@ -170,7 +171,7 @@ struct ForeachDimension {
|
|||||||
%token TOKEN_AND_OP TOKEN_OR_OP TOKEN_MUL_ASSIGN TOKEN_DIV_ASSIGN TOKEN_MOD_ASSIGN
|
%token TOKEN_AND_OP TOKEN_OR_OP TOKEN_MUL_ASSIGN TOKEN_DIV_ASSIGN TOKEN_MOD_ASSIGN
|
||||||
%token TOKEN_ADD_ASSIGN TOKEN_SUB_ASSIGN TOKEN_LEFT_ASSIGN TOKEN_RIGHT_ASSIGN
|
%token TOKEN_ADD_ASSIGN TOKEN_SUB_ASSIGN TOKEN_LEFT_ASSIGN TOKEN_RIGHT_ASSIGN
|
||||||
%token TOKEN_AND_ASSIGN TOKEN_OR_ASSIGN TOKEN_XOR_ASSIGN
|
%token TOKEN_AND_ASSIGN TOKEN_OR_ASSIGN TOKEN_XOR_ASSIGN
|
||||||
%token TOKEN_SIZEOF
|
%token TOKEN_SIZEOF TOKEN_NEW TOKEN_DELETE
|
||||||
|
|
||||||
%token TOKEN_EXTERN TOKEN_EXPORT TOKEN_STATIC TOKEN_INLINE TOKEN_TASK
|
%token TOKEN_EXTERN TOKEN_EXPORT TOKEN_STATIC TOKEN_INLINE TOKEN_TASK
|
||||||
%token TOKEN_UNIFORM TOKEN_VARYING TOKEN_TYPEDEF TOKEN_SOA
|
%token TOKEN_UNIFORM TOKEN_VARYING TOKEN_TYPEDEF TOKEN_SOA
|
||||||
@@ -189,7 +190,7 @@ struct ForeachDimension {
|
|||||||
%type <expr> multiplicative_expression additive_expression shift_expression
|
%type <expr> multiplicative_expression additive_expression shift_expression
|
||||||
%type <expr> relational_expression equality_expression and_expression
|
%type <expr> relational_expression equality_expression and_expression
|
||||||
%type <expr> exclusive_or_expression inclusive_or_expression
|
%type <expr> exclusive_or_expression inclusive_or_expression
|
||||||
%type <expr> logical_and_expression logical_or_expression
|
%type <expr> logical_and_expression logical_or_expression new_expression
|
||||||
%type <expr> conditional_expression assignment_expression expression
|
%type <expr> conditional_expression assignment_expression expression
|
||||||
%type <expr> initializer constant_expression for_test
|
%type <expr> initializer constant_expression for_test
|
||||||
%type <exprList> argument_expression_list initializer_list
|
%type <exprList> argument_expression_list initializer_list
|
||||||
@@ -197,7 +198,7 @@ struct ForeachDimension {
|
|||||||
%type <stmt> statement labeled_statement compound_statement for_init_statement
|
%type <stmt> statement labeled_statement compound_statement for_init_statement
|
||||||
%type <stmt> expression_statement selection_statement iteration_statement
|
%type <stmt> expression_statement selection_statement iteration_statement
|
||||||
%type <stmt> jump_statement statement_list declaration_statement print_statement
|
%type <stmt> jump_statement statement_list declaration_statement print_statement
|
||||||
%type <stmt> assert_statement sync_statement
|
%type <stmt> assert_statement sync_statement delete_statement
|
||||||
|
|
||||||
%type <declaration> declaration parameter_declaration
|
%type <declaration> declaration parameter_declaration
|
||||||
%type <declarators> init_declarator_list
|
%type <declarators> init_declarator_list
|
||||||
@@ -215,7 +216,7 @@ struct ForeachDimension {
|
|||||||
%type <enumType> enum_specifier
|
%type <enumType> enum_specifier
|
||||||
|
|
||||||
%type <type> specifier_qualifier_list struct_or_union_specifier
|
%type <type> specifier_qualifier_list struct_or_union_specifier
|
||||||
%type <type> type_specifier type_name
|
%type <type> type_specifier type_name rate_qualified_new_type
|
||||||
%type <type> short_vec_specifier
|
%type <type> short_vec_specifier
|
||||||
%type <atomicType> atomic_var_type_specifier
|
%type <atomicType> atomic_var_type_specifier
|
||||||
|
|
||||||
@@ -225,7 +226,7 @@ struct ForeachDimension {
|
|||||||
|
|
||||||
%type <stringVal> string_constant
|
%type <stringVal> string_constant
|
||||||
%type <constCharPtr> struct_or_union_name enum_identifier goto_identifier
|
%type <constCharPtr> struct_or_union_name enum_identifier goto_identifier
|
||||||
%type <intVal> int_constant soa_width_specifier
|
%type <intVal> int_constant soa_width_specifier rate_qualified_new
|
||||||
|
|
||||||
%type <foreachDimension> foreach_dimension_specifier
|
%type <foreachDimension> foreach_dimension_specifier
|
||||||
%type <foreachDimensionList> foreach_dimension_list
|
%type <foreachDimensionList> foreach_dimension_list
|
||||||
@@ -448,8 +449,36 @@ conditional_expression
|
|||||||
{ $$ = new SelectExpr($1, $3, $5, Union(@1,@5)); }
|
{ $$ = new SelectExpr($1, $3, $5, Union(@1,@5)); }
|
||||||
;
|
;
|
||||||
|
|
||||||
assignment_expression
|
rate_qualified_new
|
||||||
|
: TOKEN_NEW { $$ = 0; }
|
||||||
|
| TOKEN_UNIFORM TOKEN_NEW { $$ = TYPEQUAL_UNIFORM; }
|
||||||
|
| TOKEN_VARYING TOKEN_NEW { $$ = TYPEQUAL_VARYING; }
|
||||||
|
;
|
||||||
|
|
||||||
|
rate_qualified_new_type
|
||||||
|
: type_specifier { $$ = $1; }
|
||||||
|
| TOKEN_UNIFORM type_specifier { $$ = $2->GetAsUniformType(); }
|
||||||
|
| TOKEN_VARYING type_specifier { $$ = $2->GetAsVaryingType(); }
|
||||||
|
;
|
||||||
|
|
||||||
|
new_expression
|
||||||
: conditional_expression
|
: conditional_expression
|
||||||
|
| rate_qualified_new rate_qualified_new_type
|
||||||
|
{
|
||||||
|
$$ = new NewExpr($1, $2, NULL, NULL, @1, Union(@1, @2));
|
||||||
|
}
|
||||||
|
| rate_qualified_new rate_qualified_new_type '(' initializer_list ')'
|
||||||
|
{
|
||||||
|
$$ = new NewExpr($1, $2, $4, NULL, @1, Union(@1, @2));
|
||||||
|
}
|
||||||
|
| rate_qualified_new rate_qualified_new_type '[' expression ']'
|
||||||
|
{
|
||||||
|
$$ = new NewExpr($1, $2, NULL, $4, @1, Union(@1, @4));
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
assignment_expression
|
||||||
|
: new_expression
|
||||||
| unary_expression '=' assignment_expression
|
| unary_expression '=' assignment_expression
|
||||||
{ $$ = new AssignExpr(AssignExpr::Assign, $1, $3, Union(@1, @3)); }
|
{ $$ = new AssignExpr(AssignExpr::Assign, $1, $3, Union(@1, @3)); }
|
||||||
| unary_expression TOKEN_MUL_ASSIGN assignment_expression
|
| unary_expression TOKEN_MUL_ASSIGN assignment_expression
|
||||||
@@ -1240,6 +1269,7 @@ statement
|
|||||||
| print_statement
|
| print_statement
|
||||||
| assert_statement
|
| assert_statement
|
||||||
| sync_statement
|
| sync_statement
|
||||||
|
| delete_statement
|
||||||
| error
|
| error
|
||||||
{
|
{
|
||||||
std::vector<std::string> builtinTokens;
|
std::vector<std::string> builtinTokens;
|
||||||
@@ -1473,6 +1503,13 @@ sync_statement
|
|||||||
{ $$ = new ExprStmt(new SyncExpr(@1), @1); }
|
{ $$ = new ExprStmt(new SyncExpr(@1), @1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
delete_statement
|
||||||
|
: TOKEN_DELETE expression ';'
|
||||||
|
{
|
||||||
|
$$ = new DeleteStmt($2, Union(@1, @2));
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
print_statement
|
print_statement
|
||||||
: TOKEN_PRINT '(' string_constant ')' ';'
|
: TOKEN_PRINT '(' string_constant ')' ';'
|
||||||
{
|
{
|
||||||
|
|||||||
240
stmt.cpp
240
stmt.cpp
@@ -119,153 +119,6 @@ DeclStmt::DeclStmt(const std::vector<VariableDeclaration> &v, SourcePos p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
|
||||||
lPossiblyResolveFunctionOverloads(Expr *expr, const Type *type) {
|
|
||||||
FunctionSymbolExpr *fse = NULL;
|
|
||||||
const FunctionType *funcType = NULL;
|
|
||||||
if (dynamic_cast<const PointerType *>(type) != NULL &&
|
|
||||||
(funcType = dynamic_cast<const FunctionType *>(type->GetBaseType())) &&
|
|
||||||
(fse = dynamic_cast<FunctionSymbolExpr *>(expr)) != NULL) {
|
|
||||||
// We're initializing a function pointer with a function symbol,
|
|
||||||
// which in turn may represent an overloaded function. So we need
|
|
||||||
// to try to resolve the overload based on the type of the symbol
|
|
||||||
// we're initializing here.
|
|
||||||
std::vector<const Type *> paramTypes;
|
|
||||||
for (int i = 0; i < funcType->GetNumParameters(); ++i)
|
|
||||||
paramTypes.push_back(funcType->GetParameterType(i));
|
|
||||||
|
|
||||||
if (fse->ResolveOverloads(expr->pos, paramTypes) == false)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Utility routine that emits code to initialize a symbol given an
|
|
||||||
initializer expression.
|
|
||||||
|
|
||||||
@param lvalue Memory location of storage for the symbol's data
|
|
||||||
@param symName Name of symbol (used in error messages)
|
|
||||||
@param symType Type of variable being initialized
|
|
||||||
@param initExpr Expression for the initializer
|
|
||||||
@param ctx FunctionEmitContext to use for generating instructions
|
|
||||||
@param pos Source file position of the variable being initialized
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
lInitSymbol(llvm::Value *lvalue, const char *symName, const Type *symType,
|
|
||||||
Expr *initExpr, FunctionEmitContext *ctx, SourcePos pos) {
|
|
||||||
if (initExpr == NULL)
|
|
||||||
// leave it uninitialized
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If the initializer is a straight up expression that isn't an
|
|
||||||
// ExprList, then we'll see if we can type convert it to the type of
|
|
||||||
// the variable.
|
|
||||||
if (dynamic_cast<ExprList *>(initExpr) == NULL) {
|
|
||||||
if (lPossiblyResolveFunctionOverloads(initExpr, symType) == false)
|
|
||||||
return;
|
|
||||||
initExpr = TypeConvertExpr(initExpr, symType, "initializer");
|
|
||||||
|
|
||||||
if (initExpr != NULL) {
|
|
||||||
llvm::Value *initializerValue = initExpr->GetValue(ctx);
|
|
||||||
if (initializerValue != NULL)
|
|
||||||
// Bingo; store the value in the variable's storage
|
|
||||||
ctx->StoreInst(initializerValue, lvalue);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Atomic types and enums can't be initialized with { ... } initializer
|
|
||||||
// expressions, so print an error and return if that's what we've got
|
|
||||||
// here..
|
|
||||||
if (dynamic_cast<const AtomicType *>(symType) != NULL ||
|
|
||||||
dynamic_cast<const EnumType *>(symType) != NULL ||
|
|
||||||
dynamic_cast<const PointerType *>(symType) != NULL) {
|
|
||||||
ExprList *elist = dynamic_cast<ExprList *>(initExpr);
|
|
||||||
if (elist != NULL) {
|
|
||||||
if (elist->exprs.size() == 1)
|
|
||||||
lInitSymbol(lvalue, symName, symType, elist->exprs[0], ctx,
|
|
||||||
pos);
|
|
||||||
else
|
|
||||||
Error(initExpr->pos, "Expression list initializers can't be used for "
|
|
||||||
"variable \"%s\' with type \"%s\".", symName,
|
|
||||||
symType->GetString().c_str());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ReferenceType *rt = dynamic_cast<const ReferenceType *>(symType);
|
|
||||||
if (rt) {
|
|
||||||
if (!Type::Equal(initExpr->GetType(), rt)) {
|
|
||||||
Error(initExpr->pos, "Initializer for reference type \"%s\" must have same "
|
|
||||||
"reference type itself. \"%s\" is incompatible.",
|
|
||||||
rt->GetString().c_str(), initExpr->GetType()->GetString().c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Value *initializerValue = initExpr->GetValue(ctx);
|
|
||||||
if (initializerValue)
|
|
||||||
ctx->StoreInst(initializerValue, lvalue);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There are two cases for initializing structs, arrays and vectors;
|
|
||||||
// either a single initializer may be provided (float foo[3] = 0;), in
|
|
||||||
// which case all of the elements are initialized to the given value,
|
|
||||||
// or an initializer list may be provided (float foo[3] = { 1,2,3 }),
|
|
||||||
// in which case the elements are initialized with the corresponding
|
|
||||||
// values.
|
|
||||||
const CollectionType *collectionType =
|
|
||||||
dynamic_cast<const CollectionType *>(symType);
|
|
||||||
if (collectionType != NULL) {
|
|
||||||
std::string name;
|
|
||||||
if (dynamic_cast<const StructType *>(symType) != NULL)
|
|
||||||
name = "struct";
|
|
||||||
else if (dynamic_cast<const ArrayType *>(symType) != NULL)
|
|
||||||
name = "array";
|
|
||||||
else if (dynamic_cast<const VectorType *>(symType) != NULL)
|
|
||||||
name = "vector";
|
|
||||||
else
|
|
||||||
FATAL("Unexpected CollectionType in lInitSymbol()");
|
|
||||||
|
|
||||||
ExprList *exprList = dynamic_cast<ExprList *>(initExpr);
|
|
||||||
if (exprList != NULL) {
|
|
||||||
// The { ... } case; make sure we have the same number of
|
|
||||||
// expressions in the ExprList as we have struct members
|
|
||||||
int nInits = exprList->exprs.size();
|
|
||||||
if (nInits != collectionType->GetElementCount()) {
|
|
||||||
Error(initExpr->pos, "Initializer for %s \"%s\" requires "
|
|
||||||
"%d values; %d provided.", name.c_str(), symName,
|
|
||||||
collectionType->GetElementCount(), nInits);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize each element with the corresponding value from
|
|
||||||
// the ExprList
|
|
||||||
for (int i = 0; i < nInits; ++i) {
|
|
||||||
llvm::Value *ep;
|
|
||||||
if (dynamic_cast<const StructType *>(symType) != NULL)
|
|
||||||
ep = ctx->AddElementOffset(lvalue, i, NULL, "element");
|
|
||||||
else
|
|
||||||
ep = ctx->GetElementPtrInst(lvalue, LLVMInt32(0), LLVMInt32(i),
|
|
||||||
PointerType::GetUniform(collectionType->GetElementType(i)),
|
|
||||||
"gep");
|
|
||||||
|
|
||||||
lInitSymbol(ep, symName, collectionType->GetElementType(i),
|
|
||||||
exprList->exprs[i], ctx, pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Error(initExpr->pos, "Can't assign type \"%s\" to \"%s\".",
|
|
||||||
initExpr->GetType()->GetString().c_str(),
|
|
||||||
collectionType->GetString().c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FATAL("Unexpected Type in lInitSymbol()");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
lHasUnsizedArrays(const Type *type) {
|
lHasUnsizedArrays(const Type *type) {
|
||||||
const ArrayType *at = dynamic_cast<const ArrayType *>(type);
|
const ArrayType *at = dynamic_cast<const ArrayType *>(type);
|
||||||
@@ -333,7 +186,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
|||||||
// zero value.
|
// zero value.
|
||||||
llvm::Constant *cinit = NULL;
|
llvm::Constant *cinit = NULL;
|
||||||
if (initExpr != NULL) {
|
if (initExpr != NULL) {
|
||||||
if (lPossiblyResolveFunctionOverloads(initExpr, sym->type) == false)
|
if (PossiblyResolveFunctionOverloads(initExpr, sym->type) == false)
|
||||||
continue;
|
continue;
|
||||||
// FIXME: we only need this for function pointers; it was
|
// FIXME: we only need this for function pointers; it was
|
||||||
// already done for atomic types and enums in
|
// already done for atomic types and enums in
|
||||||
@@ -377,8 +230,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
|||||||
|
|
||||||
// And then get it initialized...
|
// And then get it initialized...
|
||||||
sym->parentFunction = ctx->GetFunction();
|
sym->parentFunction = ctx->GetFunction();
|
||||||
lInitSymbol(sym->storagePtr, sym->name.c_str(), sym->type,
|
InitSymbol(sym->storagePtr, sym->type, initExpr, ctx, sym->pos);
|
||||||
initExpr, ctx, sym->pos);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -646,6 +498,15 @@ lCheckAllOffSafety(ASTNode *node, void *data) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dynamic_cast<NewExpr *>(node) != NULL ||
|
||||||
|
dynamic_cast<DeleteStmt *>(node) != NULL) {
|
||||||
|
// We definitely don't want to run the uniform variants of these if
|
||||||
|
// the mask is all off. It's also worth skipping the overhead of
|
||||||
|
// executing the varying versions of them in the all-off mask case.
|
||||||
|
*okPtr = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (g->target.allOffMaskIsSafe == true)
|
if (g->target.allOffMaskIsSafe == true)
|
||||||
// Don't worry about memory accesses if we have a target that can
|
// Don't worry about memory accesses if we have a target that can
|
||||||
// safely run them with the mask all off
|
// safely run them with the mask all off
|
||||||
@@ -2880,3 +2741,82 @@ AssertStmt::EstimateCost() const {
|
|||||||
return COST_ASSERT;
|
return COST_ASSERT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// DeleteStmt
|
||||||
|
|
||||||
|
DeleteStmt::DeleteStmt(Expr *e, SourcePos p)
|
||||||
|
: Stmt(p) {
|
||||||
|
expr = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DeleteStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||||
|
const Type *exprType;
|
||||||
|
if (expr == NULL || ((exprType = expr->GetType()) == NULL)) {
|
||||||
|
Assert(m->errorCount > 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *exprValue = expr->GetValue(ctx);
|
||||||
|
if (exprValue == NULL) {
|
||||||
|
Assert(m->errorCount > 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Typechecking should catch this
|
||||||
|
Assert(dynamic_cast<const PointerType *>(exprType) != NULL);
|
||||||
|
|
||||||
|
if (exprType->IsUniformType()) {
|
||||||
|
// For deletion of a uniform pointer, we just need to cast the
|
||||||
|
// pointer type to a void pointer type, to match what
|
||||||
|
// __delete_uniform() from the builtins expects.
|
||||||
|
exprValue = ctx->BitCastInst(exprValue, LLVMTypes::VoidPointerType,
|
||||||
|
"ptr_to_void");
|
||||||
|
llvm::Function *func = m->module->getFunction("__delete_uniform");
|
||||||
|
Assert(func != NULL);
|
||||||
|
|
||||||
|
ctx->CallInst(func, NULL, exprValue, "");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Varying pointers are arrays of ints, and __delete_varying()
|
||||||
|
// takes a vector of i64s (even for 32-bit targets). Therefore, we
|
||||||
|
// only need to extend to 64-bit values on 32-bit targets before
|
||||||
|
// calling it.
|
||||||
|
llvm::Function *func = m->module->getFunction("__delete_varying");
|
||||||
|
Assert(func != NULL);
|
||||||
|
if (g->target.is32Bit)
|
||||||
|
exprValue = ctx->ZExtInst(exprValue, LLVMTypes::Int64VectorType,
|
||||||
|
"ptr_to_64");
|
||||||
|
ctx->CallInst(func, NULL, exprValue, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DeleteStmt::Print(int indent) const {
|
||||||
|
printf("%*cDelete Stmt", indent, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Stmt *
|
||||||
|
DeleteStmt::TypeCheck() {
|
||||||
|
const Type *exprType;
|
||||||
|
if (expr == NULL || ((exprType = expr->GetType()) == NULL))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (dynamic_cast<const PointerType *>(exprType) == NULL) {
|
||||||
|
Error(pos, "Illegal to delete non-pointer type \"%s\".",
|
||||||
|
exprType->GetString().c_str());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
DeleteStmt::EstimateCost() const {
|
||||||
|
return COST_DELETE;
|
||||||
|
}
|
||||||
|
|||||||
17
stmt.h
17
stmt.h
@@ -442,4 +442,21 @@ public:
|
|||||||
Expr *expr;
|
Expr *expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** Representation of a delete statement in the program.
|
||||||
|
*/
|
||||||
|
class DeleteStmt : public Stmt {
|
||||||
|
public:
|
||||||
|
DeleteStmt(Expr *e, SourcePos p);
|
||||||
|
|
||||||
|
void EmitCode(FunctionEmitContext *ctx) const;
|
||||||
|
void Print(int indent) const;
|
||||||
|
|
||||||
|
Stmt *TypeCheck();
|
||||||
|
int EstimateCost() const;
|
||||||
|
|
||||||
|
/** Expression that gives the pointer value to be deleted. */
|
||||||
|
Expr *expr;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // ISPC_STMT_H
|
#endif // ISPC_STMT_H
|
||||||
|
|||||||
15
tests/new-delete-1.ispc
Normal file
15
tests/new-delete-1.ispc
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
export uniform int width() { return programCount; }
|
||||||
|
|
||||||
|
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||||
|
float a = aFOO[programIndex];
|
||||||
|
float * uniform buf = uniform new float[programCount];
|
||||||
|
for (uniform int i = 0; i < programCount; ++i)
|
||||||
|
buf[i] = i;
|
||||||
|
RET[programIndex] = buf[a-1];
|
||||||
|
delete buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
export void result(uniform float RET[]) {
|
||||||
|
RET[programIndex] = programIndex;
|
||||||
|
}
|
||||||
15
tests/new-delete-2.ispc
Normal file
15
tests/new-delete-2.ispc
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
export uniform int width() { return programCount; }
|
||||||
|
|
||||||
|
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||||
|
float a = aFOO[programIndex];
|
||||||
|
uniform float * uniform buf = uniform new uniform float[programCount];
|
||||||
|
for (uniform int i = 0; i < programCount; ++i)
|
||||||
|
buf[i] = i;
|
||||||
|
RET[programIndex] = buf[a-1];
|
||||||
|
delete buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
export void result(uniform float RET[]) {
|
||||||
|
RET[programIndex] = programIndex;
|
||||||
|
}
|
||||||
17
tests/new-delete-3.ispc
Normal file
17
tests/new-delete-3.ispc
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export uniform int width() { return programCount; }
|
||||||
|
|
||||||
|
|
||||||
|
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||||
|
float a = aFOO[programIndex];
|
||||||
|
float * uniform buf = uniform new float[programCount+1];
|
||||||
|
for (uniform int i = 0; i < programCount+1; ++i) {
|
||||||
|
buf[i] = i+a;
|
||||||
|
}
|
||||||
|
RET[programIndex] = buf[a];
|
||||||
|
delete buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
export void result(uniform float RET[]) {
|
||||||
|
RET[programIndex] = 2 + 2*programIndex;
|
||||||
|
}
|
||||||
14
tests/new-delete-4.ispc
Normal file
14
tests/new-delete-4.ispc
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
export uniform int width() { return programCount; }
|
||||||
|
|
||||||
|
|
||||||
|
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||||
|
float a = aFOO[programIndex];
|
||||||
|
float * uniform buf = uniform new float(2*b);
|
||||||
|
RET[programIndex] = buf[0];
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
export void result(uniform float RET[]) {
|
||||||
|
RET[programIndex] = 10;
|
||||||
|
}
|
||||||
17
tests/new-delete-5.ispc
Normal file
17
tests/new-delete-5.ispc
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export uniform int width() { return programCount; }
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
uniform float x, y, z;
|
||||||
|
};
|
||||||
|
|
||||||
|
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||||
|
float a = aFOO[programIndex];
|
||||||
|
varying Point * uniform buf = uniform new varying Point(a, b, 1234.);
|
||||||
|
RET[programIndex] = buf->y;
|
||||||
|
delete buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
export void result(uniform float RET[]) {
|
||||||
|
RET[programIndex] = 5;
|
||||||
|
}
|
||||||
17
tests/new-delete-6.ispc
Normal file
17
tests/new-delete-6.ispc
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export uniform int width() { return programCount; }
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
float x, y, z;
|
||||||
|
};
|
||||||
|
|
||||||
|
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||||
|
float a = aFOO[programIndex];
|
||||||
|
Point * varying buf = new Point(0., b, a);
|
||||||
|
RET[programIndex] = buf->z;
|
||||||
|
delete buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
export void result(uniform float RET[]) {
|
||||||
|
RET[programIndex] = 1+programIndex;
|
||||||
|
}
|
||||||
47
tests_errors/func-call-through-variable.ispc
Normal file
47
tests_errors/func-call-through-variable.ispc
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Must provide function name or function pointer for function call expression
|
||||||
|
|
||||||
|
export void saxpy_ispc(uniform int N,
|
||||||
|
uniform float scale,
|
||||||
|
uniform float X[],
|
||||||
|
uniform float Y[],
|
||||||
|
uniform float result[])
|
||||||
|
{
|
||||||
|
foreach (i = 0 ... N) {
|
||||||
|
result[i] = scale * X[i] + Y[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task void saxpy_ispc_task(uniform int N,
|
||||||
|
uniform int span,
|
||||||
|
uniform float scale,
|
||||||
|
uniform float X[],
|
||||||
|
uniform float Y[],
|
||||||
|
uniform float result[])
|
||||||
|
{
|
||||||
|
uniform int indexStart;
|
||||||
|
uniform int indexEnd;
|
||||||
|
indexStart = (taskIndex * span);
|
||||||
|
indexEnd = min(N, indexStart + (span)/8);
|
||||||
|
foreach (i = indexStart ... indexEnd) {
|
||||||
|
result[i] = scale * X[i] + Y[i];
|
||||||
|
}
|
||||||
|
uniform int k =0;
|
||||||
|
for (k=0; k<8;k++) {
|
||||||
|
indexStart = (((7-taskIndex-k)%8) * span) + k(span/8);
|
||||||
|
indexEnd = min(N, indexStart + (span)/8);
|
||||||
|
foreach (i = indexStart ... indexEnd) {
|
||||||
|
result[i] = scale * X[i] + Y[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export void saxpy_ispc_withtasks(uniform int N,
|
||||||
|
uniform float scale,
|
||||||
|
uniform float X[],
|
||||||
|
uniform float Y[],
|
||||||
|
uniform float result[])
|
||||||
|
{
|
||||||
|
|
||||||
|
uniform int span = N / 8; // 8 tasks
|
||||||
|
|
||||||
|
launch[N/span] < saxpy_ispc_task(N, span, scale, X, Y, result) >;
|
||||||
|
}
|
||||||
5
tests_errors/new-delete-1.ispc
Normal file
5
tests_errors/new-delete-1.ispc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Illegal to delete non-pointer type
|
||||||
|
|
||||||
|
void func(int a) {
|
||||||
|
delete a;
|
||||||
|
}
|
||||||
5
tests_errors/new-delete-2.ispc
Normal file
5
tests_errors/new-delete-2.ispc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Syntax error
|
||||||
|
|
||||||
|
int * func(int a) {
|
||||||
|
return const new int[a];
|
||||||
|
}
|
||||||
5
tests_errors/new-delete-3.ispc
Normal file
5
tests_errors/new-delete-3.ispc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Syntax error
|
||||||
|
|
||||||
|
int * func(int a) {
|
||||||
|
return new int[a](10);
|
||||||
|
}
|
||||||
7
tests_errors/new-delete-4.ispc
Normal file
7
tests_errors/new-delete-4.ispc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Type conversion only possible from atomic types
|
||||||
|
|
||||||
|
struct P { int x; };
|
||||||
|
|
||||||
|
int * func(P p) {
|
||||||
|
return new int[p];
|
||||||
|
}
|
||||||
5
tests_errors/new-delete-5.ispc
Normal file
5
tests_errors/new-delete-5.ispc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Illegal to provide "varying" allocation count with "uniform new" expression
|
||||||
|
|
||||||
|
int * func(int x) {
|
||||||
|
return uniform new int[x];
|
||||||
|
}
|
||||||
5
tests_errors/new-delete-6.ispc
Normal file
5
tests_errors/new-delete-6.ispc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Can't convert from varying type "int32 *" to uniform type "int32 * uniform" for return
|
||||||
|
|
||||||
|
int * uniform func(int x) {
|
||||||
|
return new int[x];
|
||||||
|
}
|
||||||
12
tests_errors/new-delete-7.ispc
Normal file
12
tests_errors/new-delete-7.ispc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Can't convert from varying type "float" to uniform type "uniform float" for initializer
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
uniform float x, y, z;
|
||||||
|
};
|
||||||
|
|
||||||
|
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||||
|
float a = aFOO[programIndex];
|
||||||
|
uniform Point * uniform buf = uniform new uniform Point(a, b, 1234.);
|
||||||
|
RET[programIndex] = buf->y;
|
||||||
|
delete buf;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user