Incorporate per-lane offsets for varying data in the front-end.
Previously, it was only in the GatherScatterFlattenOpt optimization pass that we added the per-lane offsets when we were indexing into varying data. (Specifically, the case of float foo[]; int index; foo[index], where foo is an array of varying elements rather than uniform elements.) Now, this is done in the front-end as we're first emitting code. In addition to the basic ugliness of doing this in an optimization pass, it was also error-prone to do it there, since we no longer have access to all of the type information that's around in the front-end. No functionality or performance change.
This commit is contained in:
47
ctx.cpp
47
ctx.cpp
@@ -1302,7 +1302,7 @@ FunctionEmitContext::GetElementPtrInst(llvm::Value *basePtr, llvm::Value *index0
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: do we need need to handle the case of the first index being
|
// FIXME: do we need need to handle the case of the first index being
|
||||||
// varying? It's currently needed...
|
// varying? It's not currently needed...
|
||||||
assert(!llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(index0->getType()));
|
assert(!llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(index0->getType()));
|
||||||
|
|
||||||
LLVM_TYPE_CONST llvm::Type *basePtrType = basePtr->getType();
|
LLVM_TYPE_CONST llvm::Type *basePtrType = basePtr->getType();
|
||||||
@@ -1499,6 +1499,8 @@ FunctionEmitContext::gather(llvm::Value *lvalue, llvm::Value *mask,
|
|||||||
}
|
}
|
||||||
assert(gather != NULL);
|
assert(gather != NULL);
|
||||||
|
|
||||||
|
lvalue = addVaryingOffsetsIfNeeded(lvalue, type);
|
||||||
|
|
||||||
llvm::Value *voidlvalue = BitCastInst(lvalue, LLVMTypes::VoidPointerType);
|
llvm::Value *voidlvalue = BitCastInst(lvalue, LLVMTypes::VoidPointerType);
|
||||||
llvm::Instruction *call = CallInst(gather, voidlvalue, mask, name);
|
llvm::Instruction *call = CallInst(gather, voidlvalue, mask, name);
|
||||||
// Add metadata about the source file location so that the
|
// Add metadata about the source file location so that the
|
||||||
@@ -1716,6 +1718,8 @@ FunctionEmitContext::scatter(llvm::Value *rvalue, llvm::Value *lvalue,
|
|||||||
|
|
||||||
AddInstrumentationPoint("scatter");
|
AddInstrumentationPoint("scatter");
|
||||||
|
|
||||||
|
lvalue = addVaryingOffsetsIfNeeded(lvalue, rvalueType);
|
||||||
|
|
||||||
llvm::Value *voidlvalue = BitCastInst(lvalue, LLVMTypes::VoidPointerType);
|
llvm::Value *voidlvalue = BitCastInst(lvalue, LLVMTypes::VoidPointerType);
|
||||||
std::vector<llvm::Value *> args;
|
std::vector<llvm::Value *> args;
|
||||||
args.push_back(voidlvalue);
|
args.push_back(voidlvalue);
|
||||||
@@ -2041,3 +2045,44 @@ FunctionEmitContext::SyncInst() {
|
|||||||
|
|
||||||
SetCurrentBasicBlock(bPostSync);
|
SetCurrentBasicBlock(bPostSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** When we gathering from or scattering to a varying atomic type, we need
|
||||||
|
to add an appropraite toffset 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.
|
||||||
|
*/
|
||||||
|
llvm::Value *
|
||||||
|
FunctionEmitContext::addVaryingOffsetsIfNeeded(llvm::Value *ptr, const Type *type) {
|
||||||
|
// We should only have varying pointers here, which are represented as
|
||||||
|
// arrays of pointers in ispc.
|
||||||
|
LLVM_TYPE_CONST llvm::ArrayType *at =
|
||||||
|
llvm::dyn_cast<LLVM_TYPE_CONST llvm::ArrayType>(ptr->getType());
|
||||||
|
assert(at != NULL);
|
||||||
|
LLVM_TYPE_CONST llvm::PointerType *pt =
|
||||||
|
llvm::dyn_cast<LLVM_TYPE_CONST llvm::PointerType>(at->getElementType());
|
||||||
|
assert(pt != NULL);
|
||||||
|
|
||||||
|
// If we have pointers to vector types, e.g. [8 x <8 x float> *], then
|
||||||
|
// the data we're gathering from/scattering to is varying in memory.
|
||||||
|
// If we have pointers to scalar types, e.g. [8 x float *], then the
|
||||||
|
// data is uniform in memory and doesn't need any additional offsets.
|
||||||
|
if (llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(pt->getElementType()) == false)
|
||||||
|
return ptr;
|
||||||
|
|
||||||
|
llvm::Value *varyingOffsets = llvm::UndefValue::get(LLVMTypes::Int32VectorType);
|
||||||
|
for (int i = 0; i < g->target.vectorWidth; ++i)
|
||||||
|
varyingOffsets = InsertInst(varyingOffsets, LLVMInt32(i), i,
|
||||||
|
"varying_delta");
|
||||||
|
|
||||||
|
// Cast the pointer type to the corresponding uniform type--e.g. cast
|
||||||
|
// <8 x float> * to float *s.
|
||||||
|
LLVM_TYPE_CONST llvm::Type *unifType = type->GetAsUniformType()->LLVMType(g->ctx);
|
||||||
|
LLVM_TYPE_CONST llvm::PointerType *ptrCastType =
|
||||||
|
llvm::PointerType::get(llvm::ArrayType::get(unifType, 0), 0);
|
||||||
|
ptr = BitCastInst(ptr, ptrCastType, "ptr2unif");
|
||||||
|
|
||||||
|
// And now we can do the per-lane offsets...
|
||||||
|
return GetElementPtrInst(ptr, LLVMInt32(0), varyingOffsets);
|
||||||
|
}
|
||||||
|
|||||||
1
ctx.h
1
ctx.h
@@ -525,6 +525,7 @@ private:
|
|||||||
const Type *type, const char *name);
|
const Type *type, const char *name);
|
||||||
void maskedStore(llvm::Value *rvalue, llvm::Value *lvalue,
|
void maskedStore(llvm::Value *rvalue, llvm::Value *lvalue,
|
||||||
const Type *rvalueType, llvm::Value *maskPtr);
|
const Type *rvalueType, llvm::Value *maskPtr);
|
||||||
|
llvm::Value *addVaryingOffsetsIfNeeded(llvm::Value *value, const Type *type);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ISPC_CTX_H
|
#endif // ISPC_CTX_H
|
||||||
|
|||||||
240
expr.cpp
240
expr.cpp
@@ -2522,6 +2522,78 @@ lCastUniformVectorBasePtr(llvm::Value *ptr, FunctionEmitContext *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** When computing pointer values, we need to apply a per-lane offset when
|
||||||
|
we're indexing into varying data. Consdier the following ispc code:
|
||||||
|
|
||||||
|
uniform float u[] = ...;
|
||||||
|
float v[] = ...;
|
||||||
|
int index = ...;
|
||||||
|
float a = u[index];
|
||||||
|
float b = v[index];
|
||||||
|
|
||||||
|
To compute the varying pointer that holds the addresses to load from
|
||||||
|
for u[index], we basically just need to multiply index element-wise by
|
||||||
|
sizeof(float) before doing the memory load. For v[index], we need to
|
||||||
|
do the same scaling but also need to add per-lane offsets <0,
|
||||||
|
sizeof(float), 2*sizeof(float), ...> so that the i'th lane loads the
|
||||||
|
i'th of the varying values at its index value.
|
||||||
|
|
||||||
|
This function handles figuring out when this additional offset is
|
||||||
|
needed and then incorporates it in the varying pointer value.
|
||||||
|
*/
|
||||||
|
static llvm::Value *
|
||||||
|
lAddVaryingOffsetsIfNeeded(FunctionEmitContext *ctx, llvm::Value *ptr,
|
||||||
|
const Type *returnType, const Type *indexedType) {
|
||||||
|
// If the result of the indexing isn't a varying atomic type, then
|
||||||
|
// nothing to do here.
|
||||||
|
if (returnType->IsVaryingType() == false ||
|
||||||
|
dynamic_cast<const AtomicType *>(returnType) == NULL)
|
||||||
|
return ptr;
|
||||||
|
|
||||||
|
// We should now have an array of pointer values, represing in a
|
||||||
|
// varying pointer.
|
||||||
|
LLVM_TYPE_CONST llvm::ArrayType *at =
|
||||||
|
llvm::dyn_cast<LLVM_TYPE_CONST llvm::ArrayType>(ptr->getType());
|
||||||
|
if (at == NULL)
|
||||||
|
return ptr;
|
||||||
|
LLVM_TYPE_CONST llvm::PointerType *pt =
|
||||||
|
llvm::dyn_cast<LLVM_TYPE_CONST llvm::PointerType>(at->getElementType());
|
||||||
|
assert(pt != NULL);
|
||||||
|
|
||||||
|
// If the pointers are to uniform types (e.g. ptr->getType() ==
|
||||||
|
// [8 x float *]), then we have the u[index] situation from the comment
|
||||||
|
// above, and no additional offset is needed. Otherwise we have
|
||||||
|
// pointers to varying atomic types--e.g. ptr->getType() ==
|
||||||
|
// [8 x <8 x float> *]
|
||||||
|
if (llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(pt->getElementType()) == false)
|
||||||
|
return ptr;
|
||||||
|
|
||||||
|
// But not so fast: if the reason we have a vector of pointers is that
|
||||||
|
// we're indexing into an array of uniform short-vector types, then we
|
||||||
|
// don't need the offsets.
|
||||||
|
if (dynamic_cast<const VectorType *>(indexedType) != NULL)
|
||||||
|
return ptr;
|
||||||
|
|
||||||
|
// Onward: compute the per lane offsets.
|
||||||
|
llvm::Value *varyingOffsets =
|
||||||
|
llvm::UndefValue::get(LLVMTypes::Int32VectorType);
|
||||||
|
for (int i = 0; i < g->target.vectorWidth; ++i)
|
||||||
|
varyingOffsets = ctx->InsertInst(varyingOffsets, LLVMInt32(i), i,
|
||||||
|
"varying_delta");
|
||||||
|
|
||||||
|
// Cast the pointer to the corresponding uniform pointer
|
||||||
|
// type--e.g. from [8 x <8 x float> *] to [8 x float *].
|
||||||
|
LLVM_TYPE_CONST llvm::Type *unifType =
|
||||||
|
returnType->GetAsUniformType()->LLVMType(g->ctx);
|
||||||
|
LLVM_TYPE_CONST llvm::PointerType *ptrCastType =
|
||||||
|
llvm::PointerType::get(llvm::ArrayType::get(unifType, 0), 0);
|
||||||
|
ptr = ctx->BitCastInst(ptr, ptrCastType, "ptr2unif");
|
||||||
|
|
||||||
|
// And finally add the per-lane offsets.
|
||||||
|
return ctx->GetElementPtrInst(ptr, LLVMInt32(0), varyingOffsets);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
llvm::Value *
|
llvm::Value *
|
||||||
IndexExpr::GetValue(FunctionEmitContext *ctx) const {
|
IndexExpr::GetValue(FunctionEmitContext *ctx) const {
|
||||||
const Type *arrayOrVectorType;
|
const Type *arrayOrVectorType;
|
||||||
@@ -2547,6 +2619,8 @@ IndexExpr::GetValue(FunctionEmitContext *ctx) const {
|
|||||||
ctx->StoreInst(val, ptr);
|
ctx->StoreInst(val, ptr);
|
||||||
ptr = lCastUniformVectorBasePtr(ptr, ctx);
|
ptr = lCastUniformVectorBasePtr(ptr, ctx);
|
||||||
lvalue = ctx->GetElementPtrInst(ptr, LLVMInt32(0), index->GetValue(ctx));
|
lvalue = ctx->GetElementPtrInst(ptr, LLVMInt32(0), index->GetValue(ctx));
|
||||||
|
lvalue = lAddVaryingOffsetsIfNeeded(ctx, lvalue, GetType(),
|
||||||
|
arrayOrVectorType);
|
||||||
mask = LLVMMaskAllOn;
|
mask = LLVMMaskAllOn;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -2593,19 +2667,20 @@ IndexExpr::GetBaseSymbol() const {
|
|||||||
|
|
||||||
llvm::Value *
|
llvm::Value *
|
||||||
IndexExpr::GetLValue(FunctionEmitContext *ctx) const {
|
IndexExpr::GetLValue(FunctionEmitContext *ctx) const {
|
||||||
const Type *type;
|
const Type *arrayOrVectorType;
|
||||||
if (!arrayOrVector || !index || ((type = arrayOrVector->GetType()) == NULL))
|
if (arrayOrVector == NULL || index == NULL ||
|
||||||
|
((arrayOrVectorType = arrayOrVector->GetType()) == NULL))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ctx->SetDebugPos(pos);
|
ctx->SetDebugPos(pos);
|
||||||
llvm::Value *basePtr = NULL;
|
llvm::Value *basePtr = NULL;
|
||||||
if (dynamic_cast<const ArrayType *>(type) ||
|
if (dynamic_cast<const ArrayType *>(arrayOrVectorType) ||
|
||||||
dynamic_cast<const VectorType *>(type))
|
dynamic_cast<const VectorType *>(arrayOrVectorType))
|
||||||
basePtr = arrayOrVector->GetLValue(ctx);
|
basePtr = arrayOrVector->GetLValue(ctx);
|
||||||
else {
|
else {
|
||||||
type = type->GetReferenceTarget();
|
arrayOrVectorType = arrayOrVectorType->GetReferenceTarget();
|
||||||
assert(dynamic_cast<const ArrayType *>(type) ||
|
assert(dynamic_cast<const ArrayType *>(arrayOrVectorType) ||
|
||||||
dynamic_cast<const VectorType *>(type));
|
dynamic_cast<const VectorType *>(arrayOrVectorType));
|
||||||
basePtr = arrayOrVector->GetValue(ctx);
|
basePtr = arrayOrVector->GetValue(ctx);
|
||||||
}
|
}
|
||||||
if (!basePtr)
|
if (!basePtr)
|
||||||
@@ -2614,7 +2689,8 @@ IndexExpr::GetLValue(FunctionEmitContext *ctx) const {
|
|||||||
// If the array index is a compile time constant, check to see if it
|
// If the array index is a compile time constant, check to see if it
|
||||||
// may lead to an out-of-bounds access.
|
// may lead to an out-of-bounds access.
|
||||||
ConstExpr *ce = dynamic_cast<ConstExpr *>(index);
|
ConstExpr *ce = dynamic_cast<ConstExpr *>(index);
|
||||||
const SequentialType *seqType = dynamic_cast<const SequentialType *>(type);
|
const SequentialType *seqType =
|
||||||
|
dynamic_cast<const SequentialType *>(arrayOrVectorType);
|
||||||
assert(seqType != NULL);
|
assert(seqType != NULL);
|
||||||
int nElements = seqType->GetElementCount();
|
int nElements = seqType->GetElementCount();
|
||||||
if (ce != NULL && nElements > 0) {
|
if (ce != NULL && nElements > 0) {
|
||||||
@@ -2630,7 +2706,11 @@ IndexExpr::GetLValue(FunctionEmitContext *ctx) const {
|
|||||||
basePtr = lCastUniformVectorBasePtr(basePtr, ctx);
|
basePtr = lCastUniformVectorBasePtr(basePtr, ctx);
|
||||||
|
|
||||||
ctx->SetDebugPos(pos);
|
ctx->SetDebugPos(pos);
|
||||||
return ctx->GetElementPtrInst(basePtr, LLVMInt32(0), index->GetValue(ctx));
|
llvm::Value *ptr = ctx->GetElementPtrInst(basePtr, LLVMInt32(0),
|
||||||
|
index->GetValue(ctx));
|
||||||
|
ptr = lAddVaryingOffsetsIfNeeded(ctx, ptr, GetType(), arrayOrVectorType);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2731,27 +2811,32 @@ lIdentifierToVectorElement(char id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// StructMemberExpr
|
||||||
|
|
||||||
class StructMemberExpr : public MemberExpr
|
class StructMemberExpr : public MemberExpr
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StructMemberExpr(Expr *e, const char *id, SourcePos p,
|
StructMemberExpr(Expr *e, const char *id, SourcePos p,
|
||||||
SourcePos idpos, const StructType* structType);
|
SourcePos idpos, const StructType *structType);
|
||||||
|
|
||||||
const Type* GetType() const;
|
|
||||||
|
|
||||||
|
const Type *GetType() const;
|
||||||
int getElementNumber() const;
|
int getElementNumber() const;
|
||||||
|
const Type *getElementType() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const StructType* exprStructType;
|
const StructType *exprStructType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
StructMemberExpr::StructMemberExpr(Expr *e, const char *id, SourcePos p,
|
StructMemberExpr::StructMemberExpr(Expr *e, const char *id, SourcePos p,
|
||||||
SourcePos idpos,
|
SourcePos idpos,
|
||||||
const StructType* structType)
|
const StructType *structType)
|
||||||
: MemberExpr(e, id, p, idpos), exprStructType(structType) {
|
: MemberExpr(e, id, p, idpos), exprStructType(structType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Type*
|
|
||||||
|
const Type *
|
||||||
StructMemberExpr::GetType() const {
|
StructMemberExpr::GetType() const {
|
||||||
// It's a struct, and the result type is the element
|
// It's a struct, and the result type is the element
|
||||||
// type, possibly promoted to varying if the struct type / lvalue
|
// type, possibly promoted to varying if the struct type / lvalue
|
||||||
@@ -2780,26 +2865,35 @@ StructMemberExpr::getElementNumber() const {
|
|||||||
return elementNumber;
|
return elementNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Type *
|
||||||
|
StructMemberExpr::getElementType() const {
|
||||||
|
return exprStructType->GetAsUniformType()->GetElementType(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// VectorMemberExpr
|
||||||
|
|
||||||
class VectorMemberExpr : public MemberExpr
|
class VectorMemberExpr : public MemberExpr
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VectorMemberExpr(Expr *e, const char *id, SourcePos p,
|
VectorMemberExpr(Expr *e, const char *id, SourcePos p,
|
||||||
SourcePos idpos, const VectorType* vectorType);
|
SourcePos idpos, const VectorType* vectorType);
|
||||||
|
|
||||||
~VectorMemberExpr();
|
const Type *GetType() const;
|
||||||
|
llvm::Value *GetLValue(FunctionEmitContext* ctx) const;
|
||||||
const Type* GetType() const;
|
llvm::Value *GetValue(FunctionEmitContext* ctx) const;
|
||||||
|
|
||||||
llvm::Value* GetLValue(FunctionEmitContext* ctx) const;
|
|
||||||
|
|
||||||
llvm::Value* GetValue(FunctionEmitContext* ctx) const;
|
|
||||||
|
|
||||||
int getElementNumber() const;
|
int getElementNumber() const;
|
||||||
|
const Type *getElementType() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const VectorType* exprVectorType;
|
const VectorType *exprVectorType;
|
||||||
const VectorType* memberType;
|
const VectorType *memberType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
VectorMemberExpr::VectorMemberExpr(Expr *e, const char *id, SourcePos p,
|
VectorMemberExpr::VectorMemberExpr(Expr *e, const char *id, SourcePos p,
|
||||||
SourcePos idpos,
|
SourcePos idpos,
|
||||||
const VectorType* vectorType)
|
const VectorType* vectorType)
|
||||||
@@ -2808,11 +2902,8 @@ VectorMemberExpr::VectorMemberExpr(Expr *e, const char *id, SourcePos p,
|
|||||||
identifier.length());
|
identifier.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorMemberExpr::~VectorMemberExpr() {
|
|
||||||
delete memberType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Type*
|
const Type *
|
||||||
VectorMemberExpr::GetType() const {
|
VectorMemberExpr::GetType() const {
|
||||||
// For 1-element expressions, we have the base vector element
|
// For 1-element expressions, we have the base vector element
|
||||||
// type. For n-element expressions, we have a shortvec type
|
// type. For n-element expressions, we have a shortvec type
|
||||||
@@ -2826,7 +2917,7 @@ VectorMemberExpr::GetType() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
llvm::Value*
|
llvm::Value *
|
||||||
VectorMemberExpr::GetLValue(FunctionEmitContext* ctx) const {
|
VectorMemberExpr::GetLValue(FunctionEmitContext* ctx) const {
|
||||||
if (identifier.length() == 1) {
|
if (identifier.length() == 1) {
|
||||||
return MemberExpr::GetLValue(ctx);
|
return MemberExpr::GetLValue(ctx);
|
||||||
@@ -2836,11 +2927,12 @@ VectorMemberExpr::GetLValue(FunctionEmitContext* ctx) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
llvm::Value*
|
llvm::Value *
|
||||||
VectorMemberExpr::GetValue(FunctionEmitContext* ctx) const {
|
VectorMemberExpr::GetValue(FunctionEmitContext* ctx) const {
|
||||||
if (identifier.length() == 1) {
|
if (identifier.length() == 1) {
|
||||||
return MemberExpr::GetValue(ctx);
|
return MemberExpr::GetValue(ctx);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
std::vector<int> indices;
|
std::vector<int> indices;
|
||||||
|
|
||||||
for (size_t i = 0; i < identifier.size(); ++i) {
|
for (size_t i = 0; i < identifier.size(); ++i) {
|
||||||
@@ -2866,8 +2958,7 @@ VectorMemberExpr::GetValue(FunctionEmitContext* ctx) const {
|
|||||||
llvm::Value *ptmp =
|
llvm::Value *ptmp =
|
||||||
ctx->GetElementPtrInst(ltmp, 0, i, "new_offset");
|
ctx->GetElementPtrInst(ltmp, 0, i, "new_offset");
|
||||||
llvm::Value *initLValue =
|
llvm::Value *initLValue =
|
||||||
ctx->GetElementPtrInst(basePtr , 0,
|
ctx->GetElementPtrInst(basePtr, 0, indices[i], "orig_offset");
|
||||||
indices[i], "orig_offset");
|
|
||||||
llvm::Value *initValue =
|
llvm::Value *initValue =
|
||||||
ctx->LoadInst(initLValue, NULL, memberType->GetElementType(),
|
ctx->LoadInst(initLValue, NULL, memberType->GetElementType(),
|
||||||
"vec_element");
|
"vec_element");
|
||||||
@@ -2878,6 +2969,7 @@ VectorMemberExpr::GetValue(FunctionEmitContext* ctx) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
VectorMemberExpr::getElementNumber() const {
|
VectorMemberExpr::getElementNumber() const {
|
||||||
int elementNumber = lIdentifierToVectorElement(identifier[0]);
|
int elementNumber = lIdentifierToVectorElement(identifier[0]);
|
||||||
@@ -2887,43 +2979,51 @@ VectorMemberExpr::getElementNumber() const {
|
|||||||
return elementNumber;
|
return elementNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Type *
|
||||||
|
VectorMemberExpr::getElementType() const {
|
||||||
|
return memberType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// ReferenceMemberExpr
|
||||||
|
|
||||||
class ReferenceMemberExpr : public MemberExpr
|
class ReferenceMemberExpr : public MemberExpr
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ReferenceMemberExpr(Expr *e, const char *id, SourcePos p,
|
ReferenceMemberExpr(Expr *e, const char *id, SourcePos p,
|
||||||
SourcePos idpos, const ReferenceType* referenceType);
|
SourcePos idpos, const ReferenceType* referenceType);
|
||||||
|
|
||||||
const Type* GetType() const;
|
const Type *GetType() const;
|
||||||
|
llvm::Value *GetLValue(FunctionEmitContext* ctx) const;
|
||||||
|
|
||||||
int getElementNumber() const;
|
int getElementNumber() const;
|
||||||
|
const Type *getElementType() const;
|
||||||
llvm::Value* GetLValue(FunctionEmitContext* ctx) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ReferenceType* exprReferenceType;
|
const ReferenceType *exprReferenceType;
|
||||||
MemberExpr* dereferencedExpr;
|
MemberExpr *dereferencedExpr;
|
||||||
};
|
};
|
||||||
|
|
||||||
ReferenceMemberExpr::ReferenceMemberExpr(Expr *e, const char *id, SourcePos p,
|
ReferenceMemberExpr::ReferenceMemberExpr(Expr *e, const char *id, SourcePos p,
|
||||||
SourcePos idpos,
|
SourcePos idpos,
|
||||||
const ReferenceType* referenceType)
|
const ReferenceType *referenceType)
|
||||||
: MemberExpr(e, id, p, idpos), exprReferenceType(referenceType) {
|
: MemberExpr(e, id, p, idpos), exprReferenceType(referenceType) {
|
||||||
const Type* refTarget = exprReferenceType->GetReferenceTarget();
|
const Type *refTarget = exprReferenceType->GetReferenceTarget();
|
||||||
const StructType* structType
|
const StructType *structType = dynamic_cast<const StructType *>(refTarget);
|
||||||
= dynamic_cast<const StructType *>(refTarget);
|
const VectorType *vectorType = dynamic_cast<const VectorType *>(refTarget);
|
||||||
const VectorType* vectorType
|
|
||||||
= dynamic_cast<const VectorType *>(refTarget);
|
|
||||||
|
|
||||||
if (structType != NULL) {
|
if (structType != NULL)
|
||||||
dereferencedExpr = new StructMemberExpr(e, id, p, idpos, structType);
|
dereferencedExpr = new StructMemberExpr(e, id, p, idpos, structType);
|
||||||
} else if (vectorType != NULL) {
|
else if (vectorType != NULL)
|
||||||
dereferencedExpr = new VectorMemberExpr(e, id, p, idpos, vectorType);
|
dereferencedExpr = new VectorMemberExpr(e, id, p, idpos, vectorType);
|
||||||
} else {
|
else
|
||||||
dereferencedExpr = NULL;
|
dereferencedExpr = NULL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Type*
|
|
||||||
|
const Type *
|
||||||
ReferenceMemberExpr::GetType() const {
|
ReferenceMemberExpr::GetType() const {
|
||||||
if (dereferencedExpr == NULL) {
|
if (dereferencedExpr == NULL) {
|
||||||
Error(pos, "Can't access member of non-struct/vector type \"%s\".",
|
Error(pos, "Can't access member of non-struct/vector type \"%s\".",
|
||||||
@@ -2934,6 +3034,7 @@ ReferenceMemberExpr::GetType() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
ReferenceMemberExpr::getElementNumber() const {
|
ReferenceMemberExpr::getElementNumber() const {
|
||||||
if (dereferencedExpr == NULL) {
|
if (dereferencedExpr == NULL) {
|
||||||
@@ -2945,7 +3046,15 @@ ReferenceMemberExpr::getElementNumber() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Value*
|
|
||||||
|
const Type *
|
||||||
|
ReferenceMemberExpr::getElementType() const {
|
||||||
|
assert(dereferencedExpr != NULL);
|
||||||
|
return dereferencedExpr->getElementType();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
llvm::Value *
|
||||||
ReferenceMemberExpr::GetLValue(FunctionEmitContext* ctx) const {
|
ReferenceMemberExpr::GetLValue(FunctionEmitContext* ctx) const {
|
||||||
if (dereferencedExpr == NULL) {
|
if (dereferencedExpr == NULL) {
|
||||||
// FIXME: again I think typechecking should have caught this
|
// FIXME: again I think typechecking should have caught this
|
||||||
@@ -2965,29 +3074,35 @@ ReferenceMemberExpr::GetLValue(FunctionEmitContext* ctx) const {
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ctx->SetDebugPos(pos);
|
ctx->SetDebugPos(pos);
|
||||||
return ctx->GetElementPtrInst(basePtr, 0, elementNumber);
|
llvm::Value *ptr = ctx->GetElementPtrInst(basePtr, 0, elementNumber);
|
||||||
|
|
||||||
|
const Type *elementType = getElementType();
|
||||||
|
ptr = lAddVaryingOffsetsIfNeeded(ctx, ptr, GetType(), elementType);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MemberExpr*
|
MemberExpr *
|
||||||
MemberExpr::create(Expr *e, const char *id, SourcePos p, SourcePos idpos) {
|
MemberExpr::create(Expr *e, const char *id, SourcePos p, SourcePos idpos) {
|
||||||
const Type* exprType;
|
const Type *exprType;
|
||||||
if (e == NULL || (exprType = e->GetType()) == NULL)
|
if (e == NULL || (exprType = e->GetType()) == NULL)
|
||||||
return new MemberExpr(e, id, p, idpos);
|
return NULL;
|
||||||
|
|
||||||
const StructType* structType = dynamic_cast<const StructType*>(exprType);
|
const StructType *structType = dynamic_cast<const StructType*>(exprType);
|
||||||
if (structType != NULL)
|
if (structType != NULL)
|
||||||
return new StructMemberExpr(e, id, p, idpos, structType);
|
return new StructMemberExpr(e, id, p, idpos, structType);
|
||||||
|
|
||||||
const VectorType* vectorType = dynamic_cast<const VectorType*>(exprType);
|
const VectorType *vectorType = dynamic_cast<const VectorType*>(exprType);
|
||||||
if (vectorType != NULL)
|
if (vectorType != NULL)
|
||||||
return new VectorMemberExpr(e, id, p, idpos, vectorType);
|
return new VectorMemberExpr(e, id, p, idpos, vectorType);
|
||||||
|
|
||||||
const ReferenceType* referenceType = dynamic_cast<const ReferenceType*>(exprType);
|
const ReferenceType *referenceType = dynamic_cast<const ReferenceType*>(exprType);
|
||||||
if (referenceType != NULL)
|
if (referenceType != NULL)
|
||||||
return new ReferenceMemberExpr(e, id, p, idpos, referenceType);
|
return new ReferenceMemberExpr(e, id, p, idpos, referenceType);
|
||||||
|
|
||||||
return new MemberExpr(e, id, p, idpos);
|
FATAL("Unexpected case in MemberExpr::create()");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3024,6 +3139,8 @@ MemberExpr::GetValue(FunctionEmitContext *ctx) const {
|
|||||||
if (elementNumber == -1)
|
if (elementNumber == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
lvalue = ctx->GetElementPtrInst(ptr, 0, elementNumber);
|
lvalue = ctx->GetElementPtrInst(ptr, 0, elementNumber);
|
||||||
|
lvalue = lAddVaryingOffsetsIfNeeded(ctx, lvalue, GetType(), getElementType());
|
||||||
|
|
||||||
mask = LLVMMaskAllOn;
|
mask = LLVMMaskAllOn;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -3074,7 +3191,10 @@ MemberExpr::GetLValue(FunctionEmitContext *ctx) const {
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ctx->SetDebugPos(pos);
|
ctx->SetDebugPos(pos);
|
||||||
return ctx->GetElementPtrInst(basePtr, 0, elementNumber);
|
llvm::Value *ptr = ctx->GetElementPtrInst(basePtr, 0, elementNumber);
|
||||||
|
ptr = lAddVaryingOffsetsIfNeeded(ctx, ptr, GetType(), getElementType());
|
||||||
|
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
4
expr.h
4
expr.h
@@ -303,8 +303,8 @@ public:
|
|||||||
Expr *TypeCheck();
|
Expr *TypeCheck();
|
||||||
int EstimateCost() const;
|
int EstimateCost() const;
|
||||||
|
|
||||||
virtual int getElementNumber() const;
|
virtual int getElementNumber() const = 0;
|
||||||
|
virtual const Type *getElementType() const = 0;
|
||||||
std::string getCandidateNearMatches() const;
|
std::string getCandidateNearMatches() const;
|
||||||
|
|
||||||
Expr *expr;
|
Expr *expr;
|
||||||
|
|||||||
72
opt.cpp
72
opt.cpp
@@ -899,7 +899,7 @@ lGetTypeSize(LLVM_TYPE_CONST llvm::Type *type, llvm::Instruction *insertBefore)
|
|||||||
static llvm::Value *
|
static llvm::Value *
|
||||||
lTraverseConstantExpr(llvm::Constant *value, llvm::Value **offsetPtr,
|
lTraverseConstantExpr(llvm::Constant *value, llvm::Value **offsetPtr,
|
||||||
LLVM_TYPE_CONST llvm::Type **scaleType,
|
LLVM_TYPE_CONST llvm::Type **scaleType,
|
||||||
bool *leafIsVarying, llvm::Instruction *insertBefore) {
|
llvm::Instruction *insertBefore) {
|
||||||
llvm::GlobalVariable *gv = NULL;
|
llvm::GlobalVariable *gv = NULL;
|
||||||
llvm::ConstantExpr *ce = llvm::dyn_cast<llvm::ConstantExpr>(value);
|
llvm::ConstantExpr *ce = llvm::dyn_cast<llvm::ConstantExpr>(value);
|
||||||
if (ce != NULL) {
|
if (ce != NULL) {
|
||||||
@@ -907,7 +907,7 @@ lTraverseConstantExpr(llvm::Constant *value, llvm::Value **offsetPtr,
|
|||||||
case llvm::Instruction::BitCast:
|
case llvm::Instruction::BitCast:
|
||||||
*offsetPtr = LLVMInt32(0);
|
*offsetPtr = LLVMInt32(0);
|
||||||
return lTraverseConstantExpr(ce->getOperand(0), offsetPtr,
|
return lTraverseConstantExpr(ce->getOperand(0), offsetPtr,
|
||||||
scaleType, leafIsVarying, insertBefore);
|
scaleType, insertBefore);
|
||||||
case llvm::Instruction::GetElementPtr: {
|
case llvm::Instruction::GetElementPtr: {
|
||||||
gv = llvm::dyn_cast<llvm::GlobalVariable>(ce->getOperand(0));
|
gv = llvm::dyn_cast<llvm::GlobalVariable>(ce->getOperand(0));
|
||||||
assert(gv != NULL);
|
assert(gv != NULL);
|
||||||
@@ -943,30 +943,13 @@ lTraverseConstantExpr(llvm::Constant *value, llvm::Value **offsetPtr,
|
|||||||
if (gv == NULL)
|
if (gv == NULL)
|
||||||
gv = llvm::dyn_cast<llvm::GlobalVariable>(value);
|
gv = llvm::dyn_cast<llvm::GlobalVariable>(value);
|
||||||
|
|
||||||
if (gv != NULL) {
|
return gv;
|
||||||
// FIXME: is this broken for arrays of varying???!? (I think so).
|
|
||||||
// IF so, then why does the other copy if it work. (Or is that
|
|
||||||
// broken, too?!?!?)
|
|
||||||
if (leafIsVarying != NULL) {
|
|
||||||
LLVM_TYPE_CONST llvm::Type *pt = value->getType();
|
|
||||||
LLVM_TYPE_CONST llvm::PointerType *ptrType =
|
|
||||||
llvm::dyn_cast<LLVM_TYPE_CONST llvm::PointerType>(pt);
|
|
||||||
assert(ptrType);
|
|
||||||
LLVM_TYPE_CONST llvm::Type *eltType = ptrType->getElementType();
|
|
||||||
*leafIsVarying = llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(eltType);
|
|
||||||
//printf("decided that leaf %s varying!\n", *leafIsVarying ? "is" : "is not");
|
|
||||||
}
|
|
||||||
|
|
||||||
return gv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static llvm::Value *
|
static llvm::Value *
|
||||||
lGetOffsetForLane(int lane, llvm::Value *value, llvm::Value **offset,
|
lGetOffsetForLane(int lane, llvm::Value *value, llvm::Value **offset,
|
||||||
LLVM_TYPE_CONST llvm::Type **scaleType, bool *leafIsVarying,
|
LLVM_TYPE_CONST llvm::Type **scaleType,
|
||||||
llvm::Instruction *insertBefore) {
|
llvm::Instruction *insertBefore) {
|
||||||
if (!llvm::isa<llvm::GetElementPtrInst>(value)) {
|
if (!llvm::isa<llvm::GetElementPtrInst>(value)) {
|
||||||
assert(llvm::isa<llvm::BitCastInst>(value));
|
assert(llvm::isa<llvm::BitCastInst>(value));
|
||||||
@@ -986,15 +969,6 @@ lGetOffsetForLane(int lane, llvm::Value *value, llvm::Value **offset,
|
|||||||
value = iv->getOperand(1);
|
value = iv->getOperand(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leafIsVarying != NULL) {
|
|
||||||
LLVM_TYPE_CONST llvm::Type *pt = value->getType();
|
|
||||||
LLVM_TYPE_CONST llvm::PointerType *ptrType =
|
|
||||||
llvm::dyn_cast<LLVM_TYPE_CONST llvm::PointerType>(pt);
|
|
||||||
assert(ptrType);
|
|
||||||
LLVM_TYPE_CONST llvm::Type *eltType = ptrType->getElementType();
|
|
||||||
*leafIsVarying = llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(eltType);
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::GetElementPtrInst *gep = llvm::dyn_cast<llvm::GetElementPtrInst>(value);
|
llvm::GetElementPtrInst *gep = llvm::dyn_cast<llvm::GetElementPtrInst>(value);
|
||||||
assert(gep);
|
assert(gep);
|
||||||
|
|
||||||
@@ -1041,18 +1015,13 @@ lGetOffsetForLane(int lane, llvm::Value *value, llvm::Value **offset,
|
|||||||
deconstructs the LLVM array, storing the offset from the base pointer
|
deconstructs the LLVM array, storing the offset from the base pointer
|
||||||
as an llvm::Value for the i'th element into the i'th element of the
|
as an llvm::Value for the i'th element into the i'th element of the
|
||||||
offsets[] array passed in to the function. It returns a scale factor
|
offsets[] array passed in to the function. It returns a scale factor
|
||||||
for the offsets via *scaleType, and sets *leafIsVarying to true if the
|
for the offsets via *scaleType. The return value is either the base
|
||||||
leaf data type being indexed into is a 'varying' ispc type. The
|
pointer or the an array of pointers for the next dimension of indexing
|
||||||
return value is either the base pointer or the an array of pointers for
|
(that we'll in turn deconstruct with this function).
|
||||||
the next dimension of indexing (that we'll in turn deconstruct with
|
|
||||||
this function).
|
|
||||||
|
|
||||||
@todo All of the additional indexing magic for varying stuff should
|
|
||||||
happen in the front end.
|
|
||||||
*/
|
*/
|
||||||
static llvm::Value *
|
static llvm::Value *
|
||||||
lTraverseInsertChain(llvm::Value *ptrs, llvm::Value *offsets[ISPC_MAX_NVEC],
|
lTraverseInsertChain(llvm::Value *ptrs, llvm::Value *offsets[ISPC_MAX_NVEC],
|
||||||
LLVM_TYPE_CONST llvm::Type **scaleType, bool *leafIsVarying,
|
LLVM_TYPE_CONST llvm::Type **scaleType,
|
||||||
llvm::Instruction *insertBefore) {
|
llvm::Instruction *insertBefore) {
|
||||||
// The pointer values may be an array of constant pointers (this
|
// The pointer values may be an array of constant pointers (this
|
||||||
// happens, for example, when indexing into global arrays.) In that
|
// happens, for example, when indexing into global arrays.) In that
|
||||||
@@ -1064,8 +1033,7 @@ lTraverseInsertChain(llvm::Value *ptrs, llvm::Value *offsets[ISPC_MAX_NVEC],
|
|||||||
llvm::Value *base = NULL;
|
llvm::Value *base = NULL;
|
||||||
for (int i = 0; i < g->target.vectorWidth; ++i) {
|
for (int i = 0; i < g->target.vectorWidth; ++i) {
|
||||||
llvm::Value *b = lTraverseConstantExpr(ca->getOperand(i), &offsets[i],
|
llvm::Value *b = lTraverseConstantExpr(ca->getOperand(i), &offsets[i],
|
||||||
scaleType, leafIsVarying,
|
scaleType, insertBefore);
|
||||||
insertBefore);
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
base = b;
|
base = b;
|
||||||
else
|
else
|
||||||
@@ -1102,7 +1070,7 @@ lTraverseInsertChain(llvm::Value *ptrs, llvm::Value *offsets[ISPC_MAX_NVEC],
|
|||||||
// array being indexed into.
|
// array being indexed into.
|
||||||
llvm::Value *myNext = lGetOffsetForLane(elementIndex, ivInst->getOperand(1),
|
llvm::Value *myNext = lGetOffsetForLane(elementIndex, ivInst->getOperand(1),
|
||||||
&offsets[elementIndex], scaleType,
|
&offsets[elementIndex], scaleType,
|
||||||
leafIsVarying, insertBefore);
|
insertBefore);
|
||||||
if (nextChain == NULL)
|
if (nextChain == NULL)
|
||||||
nextChain = myNext;
|
nextChain = myNext;
|
||||||
else
|
else
|
||||||
@@ -1144,7 +1112,6 @@ static llvm::Value *
|
|||||||
lGetPtrAndOffsets(llvm::Value *ptrs, llvm::Value **basePtr,
|
lGetPtrAndOffsets(llvm::Value *ptrs, llvm::Value **basePtr,
|
||||||
llvm::Instruction *insertBefore, int eltSize) {
|
llvm::Instruction *insertBefore, int eltSize) {
|
||||||
llvm::Value *offset = LLVMInt32Vector(0);
|
llvm::Value *offset = LLVMInt32Vector(0);
|
||||||
bool firstLoop = true, leafIsVarying;
|
|
||||||
|
|
||||||
while (ptrs != NULL) {
|
while (ptrs != NULL) {
|
||||||
llvm::Value *offsets[ISPC_MAX_NVEC];
|
llvm::Value *offsets[ISPC_MAX_NVEC];
|
||||||
@@ -1153,8 +1120,7 @@ lGetPtrAndOffsets(llvm::Value *ptrs, llvm::Value **basePtr,
|
|||||||
LLVM_TYPE_CONST llvm::Type *scaleType = NULL;
|
LLVM_TYPE_CONST llvm::Type *scaleType = NULL;
|
||||||
|
|
||||||
llvm::Value *nextChain =
|
llvm::Value *nextChain =
|
||||||
lTraverseInsertChain(ptrs, offsets, &scaleType,
|
lTraverseInsertChain(ptrs, offsets, &scaleType, insertBefore);
|
||||||
firstLoop ? &leafIsVarying : NULL, insertBefore);
|
|
||||||
|
|
||||||
for (int i = 0; i < g->target.vectorWidth; ++i)
|
for (int i = 0; i < g->target.vectorWidth; ++i)
|
||||||
assert(offsets[i] != NULL);
|
assert(offsets[i] != NULL);
|
||||||
@@ -1185,22 +1151,6 @@ lGetPtrAndOffsets(llvm::Value *ptrs, llvm::Value **basePtr,
|
|||||||
*basePtr = nextChain;
|
*basePtr = nextChain;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
firstLoop = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle varying stuff...
|
|
||||||
if (leafIsVarying) {
|
|
||||||
llvm::Value *deltaVector = llvm::UndefValue::get(LLVMTypes::Int32VectorType);
|
|
||||||
for (int i = 0; i < g->target.vectorWidth; ++i) {
|
|
||||||
deltaVector =
|
|
||||||
llvm::InsertElementInst::Create(deltaVector, LLVMInt32(eltSize*i),
|
|
||||||
LLVMInt32(i), "delta", insertBefore);
|
|
||||||
lCopyMetadata(deltaVector, insertBefore);
|
|
||||||
}
|
|
||||||
offset = llvm::BinaryOperator::Create(llvm::Instruction::Add, offset,
|
|
||||||
deltaVector, "offset_varying_delta",
|
|
||||||
insertBefore);
|
|
||||||
lCopyMetadata(offset, insertBefore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return offset;
|
return offset;
|
||||||
|
|||||||
@@ -6,15 +6,17 @@ struct Foo {
|
|||||||
float x;
|
float x;
|
||||||
float f;
|
float f;
|
||||||
};
|
};
|
||||||
|
|
||||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||||
float a = aFOO[programIndex];
|
float a = aFOO[programIndex];
|
||||||
uniform Foo myFoo[3] = { { a, a }, {a, a}, {a, a} };
|
uniform Foo myFoo[3] = { { -1, -2 }, {a, -3}, {-4, -5} };
|
||||||
int i = 1;
|
int i = aFOO[0];
|
||||||
varying Foo barFoo = myFoo[i];
|
varying Foo barFoo = myFoo[i];
|
||||||
|
//CO print("% %\n", myFoo[i].x, barFoo.x);
|
||||||
RET[programIndex] = barFoo.x;
|
RET[programIndex] = barFoo.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export void result(uniform float RET[4]) {
|
export void result(uniform float RET[]) {
|
||||||
RET[programIndex] = 1+programIndex;
|
RET[programIndex] = 1+programIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ export void f_fi(uniform float RET[], uniform float a[], uniform int bFOO[]) {
|
|||||||
uniform int i;
|
uniform int i;
|
||||||
for (i = 0; i < 17; ++i) {
|
for (i = 0; i < 17; ++i) {
|
||||||
myFoo[i].x = i;
|
myFoo[i].x = i;
|
||||||
myFoo[i].f = 2*i;
|
myFoo[i].f = 17+2*i;
|
||||||
}
|
}
|
||||||
RET[programIndex] = myFoo[b/2].f;
|
RET[programIndex] = myFoo[b/2].f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export void result(uniform float RET[]) {
|
export void result(uniform float RET[]) {
|
||||||
RET[programIndex] = 2 + 2 * programIndex;
|
RET[programIndex] = 19 + 2 * programIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user