10
ast.cpp
10
ast.cpp
@@ -98,6 +98,7 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
||||
StmtList *sl;
|
||||
PrintStmt *ps;
|
||||
AssertStmt *as;
|
||||
DeleteStmt *dels;
|
||||
|
||||
if ((es = dynamic_cast<ExprStmt *>(node)) != NULL)
|
||||
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);
|
||||
else if ((as = dynamic_cast<AssertStmt *>(node)) != NULL)
|
||||
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
|
||||
FATAL("Unhandled statement type in WalkAST()");
|
||||
}
|
||||
@@ -180,6 +183,7 @@ WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc,
|
||||
DereferenceExpr *dre;
|
||||
SizeOfExpr *soe;
|
||||
AddressOfExpr *aoe;
|
||||
NewExpr *newe;
|
||||
|
||||
if ((ue = dynamic_cast<UnaryExpr *>(node)) != NULL)
|
||||
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);
|
||||
else if ((aoe = dynamic_cast<AddressOfExpr *>(node)) != NULL)
|
||||
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 ||
|
||||
dynamic_cast<ConstExpr *>(node) != NULL ||
|
||||
dynamic_cast<FunctionSymbolExpr *>(node) != NULL ||
|
||||
|
||||
@@ -391,6 +391,8 @@ lSetInternalFunctions(llvm::Module *module) {
|
||||
"__count_trailing_zeros_i64",
|
||||
"__count_leading_zeros_i32",
|
||||
"__count_leading_zeros_i64",
|
||||
"__delete_uniform",
|
||||
"__delete_varying",
|
||||
"__do_assert_uniform",
|
||||
"__do_assert_varying",
|
||||
"__do_print",
|
||||
@@ -449,6 +451,9 @@ lSetInternalFunctions(llvm::Module *module) {
|
||||
"__min_varying_uint32",
|
||||
"__min_varying_uint64",
|
||||
"__movmsk",
|
||||
"__new_uniform",
|
||||
"__new_varying32",
|
||||
"__new_varying64",
|
||||
"__num_cores",
|
||||
"__packed_load_active",
|
||||
"__packed_store_active",
|
||||
|
||||
@@ -1805,6 +1805,65 @@ ok:
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
type, this function determines whether these offsets are needed and
|
||||
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`_
|
||||
+ `Expressions`_
|
||||
|
||||
* `Dynamic Memory Allocation`_
|
||||
|
||||
+ `Control Flow`_
|
||||
|
||||
* `Conditional Statements: "if"`_
|
||||
@@ -1162,6 +1165,7 @@ in C:
|
||||
* The ``inline`` qualifier to indicate that a function should be inlined
|
||||
* Function overloading by parameter type
|
||||
* 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
|
||||
C++:
|
||||
@@ -1966,18 +1970,123 @@ operator also work as expected.
|
||||
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
|
||||
------------
|
||||
|
||||
``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
|
||||
control flow at that statement. It also provides parallel looping
|
||||
constructs, ``foreach`` and ``foreach_tiled``, all of which will be
|
||||
detailed in this section.
|
||||
|
||||
``ispc`` does not currently support ``switch`` statements or ``goto``.
|
||||
|
||||
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
|
||||
@@ -6527,3 +6674,211 @@ NullPointerExpr::EstimateCost() const {
|
||||
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
|
||||
toType. If the optional errorMsgBase and source position parameters
|
||||
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);
|
||||
|
||||
/** 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
|
||||
|
||||
2
ispc.h
2
ispc.h
@@ -418,6 +418,7 @@ enum {
|
||||
COST_ASSIGN = 1,
|
||||
COST_COHERENT_BREAK_CONTINE = 4,
|
||||
COST_COMPLEX_ARITH_OP = 4,
|
||||
COST_DELETE = 32,
|
||||
COST_DEREF = 4,
|
||||
COST_FUNCALL = 4,
|
||||
COST_FUNPTR_UNIFORM = 12,
|
||||
@@ -425,6 +426,7 @@ enum {
|
||||
COST_GATHER = 8,
|
||||
COST_GOTO = 4,
|
||||
COST_LOAD = 2,
|
||||
COST_NEW = 32,
|
||||
COST_REGULAR_BREAK_CONTINUE = 2,
|
||||
COST_RETURN = 4,
|
||||
COST_SELECT = 4,
|
||||
|
||||
3
lex.ll
3
lex.ll
@@ -93,6 +93,8 @@ continue { return TOKEN_CONTINUE; }
|
||||
creturn { return TOKEN_CRETURN; }
|
||||
default { return TOKEN_DEFAULT; }
|
||||
do { return TOKEN_DO; }
|
||||
delete { return TOKEN_DELETE; }
|
||||
delete\[\] { return TOKEN_DELETE; }
|
||||
double { return TOKEN_DOUBLE; }
|
||||
else { return TOKEN_ELSE; }
|
||||
enum { return TOKEN_ENUM; }
|
||||
@@ -112,6 +114,7 @@ int16 { return TOKEN_INT16; }
|
||||
int32 { return TOKEN_INT; }
|
||||
int64 { return TOKEN_INT64; }
|
||||
launch { return TOKEN_LAUNCH; }
|
||||
new { return TOKEN_NEW; }
|
||||
NULL { return TOKEN_NULL; }
|
||||
print { return TOKEN_PRINT; }
|
||||
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);
|
||||
|
||||
static const char *lBuiltinTokens[] = {
|
||||
"assert", "bool", "break", "case", "cbreak", "ccontinue", "cdo", "cfor",
|
||||
"cif", "cwhile", "const", "continue", "creturn", "default", "do", "double",
|
||||
"else", "enum", "export", "extern", "false", "float", "for", "foreach",
|
||||
"foreach_tiled", "goto", "if", "inline", "int", "int8", "int16",
|
||||
"int32", "int64", "launch", "NULL", "print", "return", "signed", "sizeof",
|
||||
"static", "struct", "switch", "sync", "task", "true", "typedef", "uniform",
|
||||
"unsigned", "varying", "void", "while", NULL
|
||||
"assert", "bool", "break", "case", "cbreak", "ccontinue", "cdo",
|
||||
"cfor", "cif", "cwhile", "const", "continue", "creturn", "default",
|
||||
"do", "delete", "double", "else", "enum", "export", "extern", "false",
|
||||
"float", "for", "foreach", "foreach_tiled", "goto", "if", "inline",
|
||||
"int", "int8", "int16", "int32", "int64", "launch", "new", "NULL",
|
||||
"print", "return", "signed", "sizeof", "static", "struct", "switch",
|
||||
"sync", "task", "true", "typedef", "uniform", "unsigned", "varying",
|
||||
"void", "while", NULL
|
||||
};
|
||||
|
||||
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_ADD_ASSIGN TOKEN_SUB_ASSIGN TOKEN_LEFT_ASSIGN TOKEN_RIGHT_ASSIGN
|
||||
%token TOKEN_AND_ASSIGN TOKEN_OR_ASSIGN TOKEN_XOR_ASSIGN
|
||||
%token TOKEN_SIZEOF
|
||||
%token TOKEN_SIZEOF TOKEN_NEW TOKEN_DELETE
|
||||
|
||||
%token TOKEN_EXTERN TOKEN_EXPORT TOKEN_STATIC TOKEN_INLINE TOKEN_TASK
|
||||
%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> relational_expression equality_expression and_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> initializer constant_expression for_test
|
||||
%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> expression_statement selection_statement iteration_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 <declarators> init_declarator_list
|
||||
@@ -215,7 +216,7 @@ struct ForeachDimension {
|
||||
%type <enumType> enum_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 <atomicType> atomic_var_type_specifier
|
||||
|
||||
@@ -225,7 +226,7 @@ struct ForeachDimension {
|
||||
|
||||
%type <stringVal> string_constant
|
||||
%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 <foreachDimensionList> foreach_dimension_list
|
||||
@@ -448,8 +449,36 @@ conditional_expression
|
||||
{ $$ = 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
|
||||
| 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
|
||||
{ $$ = new AssignExpr(AssignExpr::Assign, $1, $3, Union(@1, @3)); }
|
||||
| unary_expression TOKEN_MUL_ASSIGN assignment_expression
|
||||
@@ -1240,6 +1269,7 @@ statement
|
||||
| print_statement
|
||||
| assert_statement
|
||||
| sync_statement
|
||||
| delete_statement
|
||||
| error
|
||||
{
|
||||
std::vector<std::string> builtinTokens;
|
||||
@@ -1473,6 +1503,13 @@ sync_statement
|
||||
{ $$ = new ExprStmt(new SyncExpr(@1), @1); }
|
||||
;
|
||||
|
||||
delete_statement
|
||||
: TOKEN_DELETE expression ';'
|
||||
{
|
||||
$$ = new DeleteStmt($2, Union(@1, @2));
|
||||
}
|
||||
;
|
||||
|
||||
print_statement
|
||||
: 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
|
||||
lHasUnsizedArrays(const Type *type) {
|
||||
const ArrayType *at = dynamic_cast<const ArrayType *>(type);
|
||||
@@ -333,7 +186,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
// zero value.
|
||||
llvm::Constant *cinit = NULL;
|
||||
if (initExpr != NULL) {
|
||||
if (lPossiblyResolveFunctionOverloads(initExpr, sym->type) == false)
|
||||
if (PossiblyResolveFunctionOverloads(initExpr, sym->type) == false)
|
||||
continue;
|
||||
// FIXME: we only need this for function pointers; it was
|
||||
// already done for atomic types and enums in
|
||||
@@ -377,8 +230,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
|
||||
// And then get it initialized...
|
||||
sym->parentFunction = ctx->GetFunction();
|
||||
lInitSymbol(sym->storagePtr, sym->name.c_str(), sym->type,
|
||||
initExpr, ctx, sym->pos);
|
||||
InitSymbol(sym->storagePtr, sym->type, initExpr, ctx, sym->pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -646,6 +498,15 @@ lCheckAllOffSafety(ASTNode *node, void *data) {
|
||||
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)
|
||||
// Don't worry about memory accesses if we have a target that can
|
||||
// safely run them with the mask all off
|
||||
@@ -2880,3 +2741,82 @@ AssertStmt::EstimateCost() const {
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
/** 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
|
||||
|
||||
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