Merge branch 'master' of git://github.com/ispc/ispc

This commit is contained in:
Jean-Luc Duprat
2012-03-14 09:58:52 -07:00
204 changed files with 7419 additions and 2177 deletions

9
.gitignore vendored
View File

@@ -5,4 +5,11 @@ ispc
ispc_test
objs
docs/doxygen
docs/ispc.html
docs/*.html
tests*/*cpp
tests*/*run
examples/*/*.png
examples/*/*.ppm
examples/*/objs/*

View File

@@ -29,6 +29,9 @@ CLANG=clang
CLANG_LIBS = -lclangFrontend -lclangDriver \
-lclangSerialization -lclangParse -lclangSema \
-lclangAnalysis -lclangAST -lclangLex -lclangBasic
ifeq ($(shell llvm-config --version), 3.1svn)
CLANG_LIBS += -lclangEdit
endif
ISPC_LIBS=$(shell llvm-config --ldflags) $(CLANG_LIBS) $(LLVM_LIBS) \
-lpthread

View File

@@ -438,6 +438,12 @@ lSetInternalFunctions(llvm::Module *module) {
"__max_varying_uint32",
"__max_varying_uint64",
"__memory_barrier",
"__memcpy32",
"__memcpy64",
"__memmove32",
"__memmove64",
"__memset32",
"__memset64",
"__min_uniform_double",
"__min_uniform_float",
"__min_uniform_int32",
@@ -527,6 +533,8 @@ lSetInternalFunctions(llvm::Module *module) {
"__sqrt_uniform_float",
"__sqrt_varying_double",
"__sqrt_varying_float",
"__stdlib_acosf",
"__stdlib_asinf",
"__stdlib_atan",
"__stdlib_atan2",
"__stdlib_atan2f",
@@ -627,8 +635,9 @@ AddBitcodeToModule(const unsigned char *bitcode, int length,
static void
lDefineConstantInt(const char *name, int val, llvm::Module *module,
SymbolTable *symbolTable) {
Symbol *pw = new Symbol(name, SourcePos(), AtomicType::UniformConstInt32,
SC_STATIC);
Symbol *pw =
new Symbol(name, SourcePos(), AtomicType::UniformInt32->GetAsConstType(),
SC_STATIC);
pw->constValue = new ConstExpr(pw->type, val, SourcePos());
LLVM_TYPE_CONST llvm::Type *ltype = LLVMTypes::Int32Type;
llvm::Constant *linit = LLVMInt32(val);
@@ -661,8 +670,9 @@ lDefineConstantIntFunc(const char *name, int val, llvm::Module *module,
static void
lDefineProgramIndex(llvm::Module *module, SymbolTable *symbolTable) {
Symbol *pidx = new Symbol("programIndex", SourcePos(),
AtomicType::VaryingConstInt32, SC_STATIC);
Symbol *pidx =
new Symbol("programIndex", SourcePos(),
AtomicType::VaryingInt32->GetAsConstType(), SC_STATIC);
int pi[ISPC_MAX_NVEC];
for (int i = 0; i < g->target.vectorWidth; ++i)

View File

@@ -1768,6 +1768,55 @@ define <WIDTH x i32> @__sext_varying_bool(<WIDTH x MASK>) nounwind readnone alwa
ret <WIDTH x i32> %0')
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; memcpy/memmove/memset
declare void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src,
i32 %len, i32 %align, i1 %isvolatile)
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest, i8* %src,
i64 %len, i32 %align, i1 %isvolatile)
define void @__memcpy32(i8 * %dst, i8 * %src, i32 %len) alwaysinline {
call void @llvm.memcpy.p0i8.p0i8.i32(i8 * %dst, i8 * %src, i32 %len, i32 0, i1 0)
ret void
}
define void @__memcpy64(i8 * %dst, i8 * %src, i64 %len) alwaysinline {
call void @llvm.memcpy.p0i8.p0i8.i64(i8 * %dst, i8 * %src, i64 %len, i32 0, i1 0)
ret void
}
declare void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src,
i32 %len, i32 %align, i1 %isvolatile)
declare void @llvm.memmove.p0i8.p0i8.i64(i8* %dest, i8* %src,
i64 %len, i32 %align, i1 %isvolatile)
define void @__memmove32(i8 * %dst, i8 * %src, i32 %len) alwaysinline {
call void @llvm.memmove.p0i8.p0i8.i32(i8 * %dst, i8 * %src, i32 %len, i32 0, i1 0)
ret void
}
define void @__memmove64(i8 * %dst, i8 * %src, i64 %len) alwaysinline {
call void @llvm.memmove.p0i8.p0i8.i64(i8 * %dst, i8 * %src, i64 %len, i32 0, i1 0)
ret void
}
declare void @llvm.memset.p0i8.i32(i8* %dest, i8 %val, i32 %len, i32 %align,
i1 %isvolatile)
declare void @llvm.memset.p0i8.i64(i8* %dest, i8 %val, i64 %len, i32 %align,
i1 %isvolatile)
define void @__memset32(i8 * %dst, i8 %val, i32 %len) alwaysinline {
call void @llvm.memset.p0i8.i32(i8 * %dst, i8 %val, i32 %len, i32 0, i1 0)
ret void
}
define void @__memset64(i8 * %dst, i8 %val, i64 %len) alwaysinline {
call void @llvm.memset.p0i8.i64(i8 * %dst, i8 %val, i64 %len, i32 0, i1 0)
ret void
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; assert
@@ -1890,6 +1939,8 @@ entry:
declare float @sinf(float) nounwind readnone
declare float @cosf(float) nounwind readnone
declare void @sincosf(float, float *, float *) nounwind readnone
declare float @asinf(float) nounwind readnone
declare float @acosf(float) nounwind readnone
declare float @tanf(float) nounwind readnone
declare float @atanf(float) nounwind readnone
declare float @atan2f(float, float) nounwind readnone
@@ -1912,6 +1963,16 @@ define void @__stdlib_sincosf(float, float *, float *) nounwind readnone alwaysi
ret void
}
define float @__stdlib_asinf(float) nounwind readnone alwaysinline {
%r = call float @asinf(float %0)
ret float %r
}
define float @__stdlib_acosf(float) nounwind readnone alwaysinline {
%r = call float @acosf(float %0)
ret float %r
}
define float @__stdlib_tanf(float) nounwind readnone alwaysinline {
%r = call float @tanf(float %0)
ret float %r

View File

@@ -363,6 +363,9 @@ namespace {
bool printConstExprCast(const ConstantExpr *CE, bool Static);
void printConstantArray(ConstantArray *CPA, bool Static);
void printConstantVector(ConstantVector *CV, bool Static);
#ifdef LLVM_3_1svn
void printConstantDataSequential(ConstantDataSequential *CDS, bool Static);
#endif
/// isAddressExposed - Return true if the specified value's name needs to
/// have its address taken in order to get a C value of the correct type.
@@ -437,9 +440,11 @@ namespace {
void visitInvokeInst(InvokeInst &I) {
llvm_unreachable("Lowerinvoke pass didn't work!");
}
#if !defined(LLVM_3_1) && !defined(LLVM_3_1svn)
void visitUnwindInst(UnwindInst &I) {
llvm_unreachable("Lowerinvoke pass didn't work!");
}
#endif // !LLVM_3_1svn
void visitResumeInst(ResumeInst &I) {
llvm_unreachable("DwarfEHPrepare pass didn't work!");
}
@@ -783,7 +788,7 @@ raw_ostream &CWriter::printType(raw_ostream &Out, Type *Ty,
if (ATy->getElementType() == LLVMTypes::Int8Type) {
Out << " static " << NameSoFar << " init(const char *p) {\n";
Out << " " << NameSoFar << " ret;\n";
Out << " strncpy((char *)ret.array, p, " << NumElements << ");\n";
Out << " memcpy((uint8_t *)ret.array, (uint8_t *)p, " << NumElements << ");\n";
Out << " return ret;\n";
Out << " }\n";
}
@@ -796,19 +801,13 @@ raw_ostream &CWriter::printType(raw_ostream &Out, Type *Ty,
default:
llvm_unreachable("Unhandled case in getTypeProps!");
}
return Out;
}
void CWriter::printConstantArray(ConstantArray *CPA, bool Static) {
// As a special case, print the array as a string if it is an array of
// ubytes or an array of sbytes with positive values.
//
#ifndef LLVM_3_1svn
Type *ETy = CPA->getType()->getElementType();
// MMP: this looks like a bug: both sides of the || are the same
bool isString = (ETy == Type::getInt8Ty(CPA->getContext()) ||
ETy == Type::getInt8Ty(CPA->getContext()));
bool isString = ETy == Type::getInt8Ty(CPA->getContext());
// Make sure the last character is a null char, as automatically added by C
if (isString && (CPA->getNumOperands() == 0 ||
@@ -816,7 +815,7 @@ void CWriter::printConstantArray(ConstantArray *CPA, bool Static) {
isString = false;
if (isString) {
Out << '\"';
Out << "\"";
// Keep track of whether the last number was a hexadecimal escape.
bool LastWasHex = false;
@@ -839,49 +838,100 @@ void CWriter::printConstantArray(ConstantArray *CPA, bool Static) {
} else {
LastWasHex = false;
switch (C) {
case '\n': Out << "\\n"; break;
case '\t': Out << "\\t"; break;
case '\r': Out << "\\r"; break;
case '\v': Out << "\\v"; break;
case '\a': Out << "\\a"; break;
case '\"': Out << "\\\""; break;
case '\'': Out << "\\\'"; break;
default:
Out << "\\x";
Out << (char)(( C/16 < 10) ? ( C/16 +'0') : ( C/16 -10+'A'));
Out << (char)(((C&15) < 10) ? ((C&15)+'0') : ((C&15)-10+'A'));
LastWasHex = true;
break;
case '\n': Out << "\\n"; break;
case '\t': Out << "\\t"; break;
case '\r': Out << "\\r"; break;
case '\v': Out << "\\v"; break;
case '\a': Out << "\\a"; break;
case '\"': Out << "\\\""; break;
case '\'': Out << "\\\'"; break;
default:
Out << "\\x";
Out << (char)(( C/16 < 10) ? ( C/16 +'0') : ( C/16 -10+'A'));
Out << (char)(((C&15) < 10) ? ((C&15)+'0') : ((C&15)-10+'A'));
LastWasHex = true;
break;
}
}
}
Out << "\"";
return;
}
#endif // !LLVM_3_1
printConstant(cast<Constant>(CPA->getOperand(0)), Static);
for (unsigned i = 1, e = CPA->getNumOperands(); i != e; ++i) {
Out << ", ";
printConstant(cast<Constant>(CPA->getOperand(i)), Static);
}
}
void CWriter::printConstantVector(ConstantVector *CP, bool Static) {
printConstant(cast<Constant>(CP->getOperand(0)), Static);
for (unsigned i = 1, e = CP->getNumOperands(); i != e; ++i) {
Out << ", ";
printConstant(cast<Constant>(CP->getOperand(i)), Static);
}
}
#ifdef LLVM_3_1svn
void CWriter::printConstantDataSequential(ConstantDataSequential *CDS,
bool Static) {
// As a special case, print the array as a string if it is an array of
// ubytes or an array of sbytes with positive values.
//
if (CDS->isCString()) {
Out << '\"';
// Keep track of whether the last number was a hexadecimal escape.
bool LastWasHex = false;
StringRef Bytes = CDS->getAsCString();
// Do not include the last character, which we know is null
for (unsigned i = 0, e = Bytes.size(); i != e; ++i) {
unsigned char C = Bytes[i];
// Print it out literally if it is a printable character. The only thing
// to be careful about is when the last letter output was a hex escape
// code, in which case we have to be careful not to print out hex digits
// explicitly (the C compiler thinks it is a continuation of the previous
// character, sheesh...)
//
if (isprint(C) && (!LastWasHex || !isxdigit(C))) {
LastWasHex = false;
if (C == '"' || C == '\\')
Out << "\\" << (char)C;
else
Out << (char)C;
} else {
LastWasHex = false;
switch (C) {
case '\n': Out << "\\n"; break;
case '\t': Out << "\\t"; break;
case '\r': Out << "\\r"; break;
case '\v': Out << "\\v"; break;
case '\a': Out << "\\a"; break;
case '\"': Out << "\\\""; break;
case '\'': Out << "\\\'"; break;
default:
Out << "\\x";
Out << (char)(( C/16 < 10) ? ( C/16 +'0') : ( C/16 -10+'A'));
Out << (char)(((C&15) < 10) ? ((C&15)+'0') : ((C&15)-10+'A'));
LastWasHex = true;
break;
}
}
}
Out << '\"';
} else {
if (Static)
Out << '{';
if (CPA->getNumOperands()) {
Out << ' ';
printConstant(cast<Constant>(CPA->getOperand(0)), Static);
for (unsigned i = 1, e = CPA->getNumOperands(); i != e; ++i) {
Out << ", ";
printConstant(cast<Constant>(CPA->getOperand(i)), Static);
}
}
if (Static)
Out << " }";
}
}
void CWriter::printConstantVector(ConstantVector *CP, bool Static) {
if (CP->getNumOperands()) {
Out << ' ';
printConstant(cast<Constant>(CP->getOperand(0)), Static);
for (unsigned i = 1, e = CP->getNumOperands(); i != e; ++i) {
printConstant(CDS->getElementAsConstant(0), Static);
for (unsigned i = 1, e = CDS->getNumElements(); i != e; ++i) {
Out << ", ";
printConstant(cast<Constant>(CP->getOperand(i)), Static);
printConstant(CDS->getElementAsConstant(i), Static);
}
}
}
#endif // LLVM_3_1svn
// isFPCSafeToPrint - Returns true if we may assume that CFP may be written out
// textually as a double (rather than as a reference to a stack-allocated
@@ -1305,7 +1355,7 @@ void CWriter::printConstant(Constant *CPV, bool Static) {
char Buffer[100];
uint64_t ll = DoubleToBits(V);
sprintf(Buffer, "0x%"PRIx64, static_cast<long long>(ll));
sprintf(Buffer, "0x%"PRIx64, ll);
std::string Num(&Buffer[0], &Buffer[6]);
unsigned long Val = strtoul(Num.c_str(), 0, 16);
@@ -1350,6 +1400,11 @@ void CWriter::printConstant(Constant *CPV, bool Static) {
}
if (ConstantArray *CA = dyn_cast<ConstantArray>(CPV)) {
printConstantArray(CA, Static);
#ifdef LLVM_3_1svn
} else if (ConstantDataSequential *CDS =
dyn_cast<ConstantDataSequential>(CPV)) {
printConstantDataSequential(CDS, Static);
#endif // LLVM_3_1svn
} else {
assert(isa<ConstantAggregateZero>(CPV) || isa<UndefValue>(CPV));
if (AT->getNumElements()) {
@@ -1374,6 +1429,11 @@ void CWriter::printConstant(Constant *CPV, bool Static) {
if (ConstantVector *CV = dyn_cast<ConstantVector>(CPV)) {
printConstantVector(CV, Static);
#ifdef LLVM_3_1svn
} else if (ConstantDataSequential *CDS =
dyn_cast<ConstantDataSequential>(CPV)) {
printConstantDataSequential(CDS, Static);
#endif
} else {
assert(isa<ConstantAggregateZero>(CPV) || isa<UndefValue>(CPV));
VectorType *VT = cast<VectorType>(CPV->getType());
@@ -1995,7 +2055,6 @@ bool CWriter::doInitialization(Module &M) {
Out << "#include <setjmp.h>\n"; // Unwind support
Out << "#include <limits.h>\n"; // With overflow intrinsics support.
Out << "#include <stdlib.h>\n";
Out << "#include <string.h>\n";
Out << "#ifdef _MSC_VER\n";
Out << " #define NOMINMAX\n";
Out << " #include <windows.h>\n";
@@ -2014,6 +2073,68 @@ bool CWriter::doInitialization(Module &M) {
generateCompilerSpecificCode(Out, TD);
// Function declarations
Out << "\n/* Function Declarations */\n";
Out << "extern \"C\" {\n";
Out << "int puts(unsigned char *);\n";
Out << "unsigned int putchar(unsigned int);\n";
Out << "int fflush(void *);\n";
Out << "int printf(const unsigned char *, ...);\n";
Out << "uint8_t *memcpy(uint8_t *, uint8_t *, uint64_t );\n";
// Store the intrinsics which will be declared/defined below.
SmallVector<const Function*, 8> intrinsicsToDefine;
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
// Don't print declarations for intrinsic functions.
// Store the used intrinsics, which need to be explicitly defined.
if (I->isIntrinsic()) {
switch (I->getIntrinsicID()) {
default:
break;
case Intrinsic::uadd_with_overflow:
case Intrinsic::sadd_with_overflow:
intrinsicsToDefine.push_back(I);
break;
}
continue;
}
if (I->getName() == "setjmp" || I->getName() == "abort" ||
I->getName() == "longjmp" || I->getName() == "_setjmp" ||
I->getName() == "memset" || I->getName() == "memset_pattern16" ||
I->getName() == "puts" ||
I->getName() == "printf" || I->getName() == "putchar" ||
I->getName() == "fflush" || I->getName() == "malloc" ||
I->getName() == "free")
continue;
// Don't redeclare ispc's own intrinsics
std::string name = I->getName();
if (name.size() > 2 && name[0] == '_' && name[1] == '_')
continue;
if (I->hasExternalWeakLinkage())
Out << "extern ";
printFunctionSignature(I, true);
if (I->hasWeakLinkage() || I->hasLinkOnceLinkage())
Out << " __ATTRIBUTE_WEAK__";
if (I->hasExternalWeakLinkage())
Out << " __EXTERNAL_WEAK__";
if (StaticCtors.count(I))
Out << " __ATTRIBUTE_CTOR__";
if (StaticDtors.count(I))
Out << " __ATTRIBUTE_DTOR__";
if (I->hasHiddenVisibility())
Out << " __HIDDEN__";
if (I->hasName() && I->getName()[0] == 1)
Out << " LLVM_ASM(\"" << I->getName().substr(1) << "\")";
Out << ";\n";
}
Out << "}\n";
// Provide a definition for `bool' if not compiling with a C++ compiler.
Out << "\n"
<< "#ifndef __cplusplus\ntypedef unsigned char bool;\n#endif\n"
@@ -2083,67 +2204,6 @@ bool CWriter::doInitialization(Module &M) {
}
}
// Function declarations
Out << "\n/* Function Declarations */\n";
Out << "extern \"C\" {\n";
Out << "int puts(unsigned char *);\n";
Out << "unsigned int putchar(unsigned int);\n";
Out << "int fflush(void *);\n";
Out << "int printf(const unsigned char *, ...);\n";
// Store the intrinsics which will be declared/defined below.
SmallVector<const Function*, 8> intrinsicsToDefine;
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
// Don't print declarations for intrinsic functions.
// Store the used intrinsics, which need to be explicitly defined.
if (I->isIntrinsic()) {
switch (I->getIntrinsicID()) {
default:
break;
case Intrinsic::uadd_with_overflow:
case Intrinsic::sadd_with_overflow:
intrinsicsToDefine.push_back(I);
break;
}
continue;
}
if (I->getName() == "setjmp" || I->getName() == "abort" ||
I->getName() == "longjmp" || I->getName() == "_setjmp" ||
I->getName() == "memset" || I->getName() == "memset_pattern16" ||
I->getName() == "puts" ||
I->getName() == "printf" || I->getName() == "putchar" ||
I->getName() == "fflush" || I->getName() == "malloc" ||
I->getName() == "free")
continue;
// Don't redeclare ispc's own intrinsics
std::string name = I->getName();
if (name.size() > 2 && name[0] == '_' && name[1] == '_')
continue;
if (I->hasExternalWeakLinkage())
Out << "extern ";
printFunctionSignature(I, true);
if (I->hasWeakLinkage() || I->hasLinkOnceLinkage())
Out << " __ATTRIBUTE_WEAK__";
if (I->hasExternalWeakLinkage())
Out << " __EXTERNAL_WEAK__";
if (StaticCtors.count(I))
Out << " __ATTRIBUTE_CTOR__";
if (StaticDtors.count(I))
Out << " __ATTRIBUTE_DTOR__";
if (I->hasHiddenVisibility())
Out << " __HIDDEN__";
if (I->hasName() && I->getName()[0] == 1)
Out << " LLVM_ASM(\"" << I->getName().substr(1) << "\")";
Out << ";\n";
}
Out << "}\n";
// Output the global variable declarations
if (!M.global_empty()) {
Out << "\n\n/* Global Variable Declarations */\n";
@@ -2763,11 +2823,17 @@ void CWriter::visitSwitchInst(SwitchInst &SI) {
printBranchToBlock(SI.getParent(), SI.getDefaultDest(), 2);
Out << ";\n";
unsigned NumCases = SI.getNumCases();
#ifdef LLVM_3_1svn
for (SwitchInst::CaseIt i = SI.case_begin(), e = SI.case_end(); i != e; ++i) {
ConstantInt* CaseVal = i.getCaseValue();
BasicBlock* Succ = i.getCaseSuccessor();
#else
// Skip the first item since that's the default case.
unsigned NumCases = SI.getNumCases();
for (unsigned i = 1; i < NumCases; ++i) {
ConstantInt* CaseVal = SI.getCaseValue(i);
BasicBlock* Succ = SI.getSuccessor(i);
#endif // LLVM_3_1svn
Out << " case ";
writeOperand(CaseVal);
Out << ":\n";

698
ctx.cpp
View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2011, Intel Corporation
Copyright (c) 2010-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -246,7 +246,7 @@ FunctionEmitContext::FunctionEmitContext(Function *func, Symbol *funSym,
launchGroupHandlePtr);
const Type *returnType = function->GetReturnType();
if (!returnType || returnType == AtomicType::Void)
if (!returnType || Type::Equal(returnType, AtomicType::Void))
returnValuePtr = NULL;
else {
LLVM_TYPE_CONST llvm::Type *ftype = returnType->LLVMType(g->ctx);
@@ -1144,7 +1144,7 @@ FunctionEmitContext::GetLabeledBasicBlock(const std::string &label) {
void
FunctionEmitContext::CurrentLanesReturned(Expr *expr, bool doCoherenceCheck) {
const Type *returnType = function->GetReturnType();
if (returnType == AtomicType::Void) {
if (Type::Equal(returnType, AtomicType::Void)) {
if (expr != NULL)
Error(expr->pos, "Can't return non-void type \"%s\" from void function.",
expr->GetType()->GetString().c_str());
@@ -1169,7 +1169,7 @@ FunctionEmitContext::CurrentLanesReturned(Expr *expr, bool doCoherenceCheck) {
// values from other lanes that may have executed return
// statements previously.
StoreInst(retVal, returnValuePtr, GetInternalMask(),
PointerType::GetUniform(returnType));
returnType, PointerType::GetUniform(returnType));
}
}
}
@@ -1904,17 +1904,146 @@ FunctionEmitContext::applyVaryingGEP(llvm::Value *basePtr, llvm::Value *index,
}
void
FunctionEmitContext::MatchIntegerTypes(llvm::Value **v0, llvm::Value **v1) {
LLVM_TYPE_CONST llvm::Type *type0 = (*v0)->getType();
LLVM_TYPE_CONST llvm::Type *type1 = (*v1)->getType();
// First, promote to a vector type if one of the two values is a vector
// type
if (llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(type0) &&
!llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(type1)) {
*v1 = SmearUniform(*v1, "smear_v1");
type1 = (*v1)->getType();
}
if (!llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(type0) &&
llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(type1)) {
*v0 = SmearUniform(*v0, "smear_v0");
type0 = (*v0)->getType();
}
// And then update to match bit widths
if (type0 == LLVMTypes::Int32VectorType &&
type1 == LLVMTypes::Int64VectorType)
*v0 = SExtInst(*v0, LLVMTypes::Int64VectorType);
else if (type1 == LLVMTypes::Int32VectorType &&
type0 == LLVMTypes::Int64VectorType)
*v1 = SExtInst(*v1, LLVMTypes::Int64VectorType);
}
/** Given an integer index in indexValue that's indexing into an array of
soa<> structures with given soaWidth, compute the two sub-indices we
need to do the actual indexing calculation:
subIndices[0] = (indexValue >> log(soaWidth))
subIndices[1] = (indexValue & (soaWidth-1))
*/
static llvm::Value *
lComputeSliceIndex(FunctionEmitContext *ctx, int soaWidth,
llvm::Value *indexValue, llvm::Value *ptrSliceOffset,
llvm::Value **newSliceOffset) {
// Compute the log2 of the soaWidth.
Assert(soaWidth > 0);
int logWidth = 0, sw = soaWidth;
while (sw > 1) {
++logWidth;
sw >>= 1;
}
Assert((1 << logWidth) == soaWidth);
ctx->MatchIntegerTypes(&indexValue, &ptrSliceOffset);
LLVM_TYPE_CONST llvm::Type *indexType = indexValue->getType();
llvm::Value *shift = LLVMIntAsType(logWidth, indexType);
llvm::Value *mask = LLVMIntAsType(soaWidth-1, indexType);
llvm::Value *indexSum =
ctx->BinaryOperator(llvm::Instruction::Add, indexValue, ptrSliceOffset,
"index_sum");
// minor index = (index & (soaWidth - 1))
*newSliceOffset = ctx->BinaryOperator(llvm::Instruction::And, indexSum,
mask, "slice_index_minor");
// slice offsets are always 32 bits...
if ((*newSliceOffset)->getType() == LLVMTypes::Int64Type)
*newSliceOffset = ctx->TruncInst(*newSliceOffset, LLVMTypes::Int32Type);
else if ((*newSliceOffset)->getType() == LLVMTypes::Int64VectorType)
*newSliceOffset = ctx->TruncInst(*newSliceOffset, LLVMTypes::Int32VectorType);
// major index = (index >> logWidth)
return ctx->BinaryOperator(llvm::Instruction::AShr, indexSum,
shift, "slice_index_major");
}
llvm::Value *
FunctionEmitContext::MakeSlicePointer(llvm::Value *ptr, llvm::Value *offset) {
// Create a small struct where the first element is the type of the
// given pointer and the second element is the type of the offset
// value.
std::vector<LLVM_TYPE_CONST llvm::Type *> eltTypes;
eltTypes.push_back(ptr->getType());
eltTypes.push_back(offset->getType());
LLVM_TYPE_CONST llvm::StructType *st =
llvm::StructType::get(*g->ctx, eltTypes);
llvm::Value *ret = llvm::UndefValue::get(st);
ret = InsertInst(ret, ptr, 0);
ret = InsertInst(ret, offset, 1);
return ret;
}
llvm::Value *
FunctionEmitContext::GetElementPtrInst(llvm::Value *basePtr, llvm::Value *index,
const Type *ptrType, const char *name) {
const Type *ptrRefType, const char *name) {
if (basePtr == NULL || index == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
if (dynamic_cast<const ReferenceType *>(ptrType) != NULL)
ptrType = PointerType::GetUniform(ptrType->GetReferenceTarget());
Assert(dynamic_cast<const PointerType *>(ptrType) != NULL);
// Regularize to a standard pointer type for basePtr's type
const PointerType *ptrType;
if (dynamic_cast<const ReferenceType *>(ptrRefType) != NULL)
ptrType = PointerType::GetUniform(ptrRefType->GetReferenceTarget());
else {
ptrType = dynamic_cast<const PointerType *>(ptrRefType);
Assert(ptrType != NULL);
}
if (ptrType->IsSlice()) {
Assert(llvm::isa<LLVM_TYPE_CONST llvm::StructType>(basePtr->getType()));
llvm::Value *ptrSliceOffset = ExtractInst(basePtr, 1);
if (ptrType->IsFrozenSlice() == false) {
// For slice pointers that aren't frozen, we compute a new
// index based on the given index plus the offset in the slice
// pointer. This gives us an updated integer slice index for
// the resulting slice pointer and then an index to index into
// the soa<> structs with.
llvm::Value *newSliceOffset;
int soaWidth = ptrType->GetBaseType()->GetSOAWidth();
index = lComputeSliceIndex(this, soaWidth, index,
ptrSliceOffset, &newSliceOffset);
ptrSliceOffset = newSliceOffset;
}
// Handle the indexing into the soa<> structs with the major
// component of the index through a recursive call
llvm::Value *p = GetElementPtrInst(ExtractInst(basePtr, 0), index,
ptrType->GetAsNonSlice(), name);
// And mash the results together for the return value
return MakeSlicePointer(p, ptrSliceOffset);
}
// Double-check consistency between the given pointer type and its LLVM
// type.
if (ptrType->IsUniformType())
Assert(llvm::isa<LLVM_TYPE_CONST llvm::PointerType>(basePtr->getType()));
else if (ptrType->IsVaryingType())
Assert(llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(basePtr->getType()));
bool indexIsVaryingType =
llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(index->getType());
@@ -1943,16 +2072,41 @@ FunctionEmitContext::GetElementPtrInst(llvm::Value *basePtr, llvm::Value *index,
llvm::Value *
FunctionEmitContext::GetElementPtrInst(llvm::Value *basePtr, llvm::Value *index0,
llvm::Value *index1, const Type *ptrType,
llvm::Value *index1, const Type *ptrRefType,
const char *name) {
if (basePtr == NULL || index0 == NULL || index1 == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
if (dynamic_cast<const ReferenceType *>(ptrType) != NULL)
ptrType = PointerType::GetUniform(ptrType->GetReferenceTarget());
Assert(dynamic_cast<const PointerType *>(ptrType) != NULL);
// Regaularize the pointer type for basePtr
const PointerType *ptrType = NULL;
if (dynamic_cast<const ReferenceType *>(ptrRefType) != NULL)
ptrType = PointerType::GetUniform(ptrRefType->GetReferenceTarget());
else {
ptrType = dynamic_cast<const PointerType *>(ptrRefType);
Assert(ptrType != NULL);
}
if (ptrType->IsSlice()) {
// Similar to the 1D GEP implementation above, for non-frozen slice
// pointers we do the two-step indexing calculation and then pass
// the new major index on to a recursive GEP call.
Assert(llvm::isa<LLVM_TYPE_CONST llvm::StructType>(basePtr->getType()));
llvm::Value *ptrSliceOffset = ExtractInst(basePtr, 1);
if (ptrType->IsFrozenSlice() == false) {
llvm::Value *newSliceOffset;
int soaWidth = ptrType->GetBaseType()->GetSOAWidth();
index1 = lComputeSliceIndex(this, soaWidth, index1,
ptrSliceOffset, &newSliceOffset);
ptrSliceOffset = newSliceOffset;
}
llvm::Value *p = GetElementPtrInst(ExtractInst(basePtr, 0), index0,
index1, ptrType->GetAsNonSlice(),
name);
return MakeSlicePointer(p, ptrSliceOffset);
}
bool index0IsVaryingType =
llvm::isa<LLVM_TYPE_CONST llvm::VectorType>(index0->getType());
@@ -2000,62 +2154,113 @@ FunctionEmitContext::GetElementPtrInst(llvm::Value *basePtr, llvm::Value *index0
llvm::Value *
FunctionEmitContext::AddElementOffset(llvm::Value *basePtr, int elementNum,
const Type *ptrType, const char *name) {
if (ptrType == NULL || ptrType->IsUniformType() ||
dynamic_cast<const ReferenceType *>(ptrType) != NULL) {
// If the pointer is uniform or we have a reference (which is a
// uniform pointer in the end), we can use the regular LLVM GEP.
FunctionEmitContext::AddElementOffset(llvm::Value *fullBasePtr, int elementNum,
const Type *ptrRefType, const char *name,
const PointerType **resultPtrType) {
if (resultPtrType != NULL)
Assert(ptrRefType != NULL);
// (Unfortunately) it's not required to pass a non-NULL ptrRefType, but
// if we have one, regularize into a pointer type.
const PointerType *ptrType = NULL;
if (ptrRefType != NULL) {
// Normalize references to uniform pointers
if (dynamic_cast<const ReferenceType *>(ptrRefType) != NULL)
ptrType = PointerType::GetUniform(ptrRefType->GetReferenceTarget());
else
ptrType = dynamic_cast<const PointerType *>(ptrRefType);
Assert(ptrType != NULL);
}
// Similarly, we have to see if the pointer type is a struct to see if
// we have a slice pointer instead of looking at ptrType; this is also
// unfortunate...
llvm::Value *basePtr = fullBasePtr;
bool baseIsSlicePtr =
llvm::isa<LLVM_TYPE_CONST llvm::StructType>(fullBasePtr->getType());
const PointerType *rpt;
if (baseIsSlicePtr) {
Assert(ptrType != NULL);
// Update basePtr to just be the part that actually points to the
// start of an soa<> struct for now; the element offset computation
// doesn't change the slice offset, so we'll incorporate that into
// the final value right before this method returns.
basePtr = ExtractInst(fullBasePtr, 0);
if (resultPtrType == NULL)
resultPtrType = &rpt;
}
// Return the pointer type of the result of this call, for callers that
// want it.
if (resultPtrType != NULL) {
Assert(ptrType != NULL);
const CollectionType *ct =
dynamic_cast<const CollectionType *>(ptrType->GetBaseType());
Assert(ct != NULL);
*resultPtrType = new PointerType(ct->GetElementType(elementNum),
ptrType->GetVariability(),
ptrType->IsConstType(),
ptrType->IsSlice());
}
llvm::Value *resultPtr = NULL;
if (ptrType == NULL || ptrType->IsUniformType()) {
// If the pointer is uniform, we can use the regular LLVM GEP.
llvm::Value *offsets[2] = { LLVMInt32(0), LLVMInt32(elementNum) };
#if defined(LLVM_3_0) || defined(LLVM_3_0svn) || defined(LLVM_3_1svn)
llvm::ArrayRef<llvm::Value *> arrayRef(&offsets[0], &offsets[2]);
return llvm::GetElementPtrInst::Create(basePtr, arrayRef,
name ? name : "struct_offset", bblock);
resultPtr =
llvm::GetElementPtrInst::Create(basePtr, arrayRef,
name ? name : "struct_offset", bblock);
#else
return llvm::GetElementPtrInst::Create(basePtr, &offsets[0], &offsets[2],
name ? name : "struct_offset", bblock);
resultPtr =
llvm::GetElementPtrInst::Create(basePtr, &offsets[0], &offsets[2],
name ? name : "struct_offset", bblock);
#endif
}
if (dynamic_cast<const ReferenceType *>(ptrType) != NULL)
ptrType = PointerType::GetUniform(ptrType->GetReferenceTarget());
Assert(dynamic_cast<const PointerType *>(ptrType) != NULL);
// Otherwise do the math to find the offset and add it to the given
// varying pointers
const StructType *st =
dynamic_cast<const StructType *>(ptrType->GetBaseType());
llvm::Value *offset = NULL;
if (st != NULL)
// If the pointer is to a structure, Target::StructOffset() gives
// us the offset in bytes to the given element of the structure
offset = g->target.StructOffset(st->LLVMType(g->ctx), elementNum,
bblock);
else {
// Otherwise we should have a vector or array here and the offset
// is given by the element number times the size of the element
// type of the vector.
const SequentialType *st =
dynamic_cast<const SequentialType *>(ptrType->GetBaseType());
Assert(st != NULL);
llvm::Value *size =
g->target.SizeOf(st->GetElementType()->LLVMType(g->ctx), bblock);
llvm::Value *scale = (g->target.is32Bit || g->opt.force32BitAddressing) ?
LLVMInt32(elementNum) : LLVMInt64(elementNum);
offset = BinaryOperator(llvm::Instruction::Mul, size, scale);
// Otherwise do the math to find the offset and add it to the given
// varying pointers
const StructType *st =
dynamic_cast<const StructType *>(ptrType->GetBaseType());
llvm::Value *offset = NULL;
if (st != NULL)
// If the pointer is to a structure, Target::StructOffset() gives
// us the offset in bytes to the given element of the structure
offset = g->target.StructOffset(st->LLVMType(g->ctx), elementNum,
bblock);
else {
// Otherwise we should have a vector or array here and the offset
// is given by the element number times the size of the element
// type of the vector.
const SequentialType *st =
dynamic_cast<const SequentialType *>(ptrType->GetBaseType());
Assert(st != NULL);
llvm::Value *size =
g->target.SizeOf(st->GetElementType()->LLVMType(g->ctx), bblock);
llvm::Value *scale = (g->target.is32Bit || g->opt.force32BitAddressing) ?
LLVMInt32(elementNum) : LLVMInt64(elementNum);
offset = BinaryOperator(llvm::Instruction::Mul, size, scale);
}
offset = SmearUniform(offset, "offset_smear");
if (g->target.is32Bit == false && g->opt.force32BitAddressing == true)
// If we're doing 32 bit addressing with a 64 bit target, although
// we did the math above in 32 bit, we need to go to 64 bit before
// we add the offset to the varying pointers.
offset = SExtInst(offset, LLVMTypes::Int64VectorType, "offset_to_64");
resultPtr = BinaryOperator(llvm::Instruction::Add, basePtr, offset,
"struct_ptr_offset");
}
offset = SmearUniform(offset, "offset_smear");
if (g->target.is32Bit == false && g->opt.force32BitAddressing == true)
// If we're doing 32 bit addressing with a 64 bit target, although
// we did the math above in 32 bit, we need to go to 64 bit before
// we add the offset to the varying pointers.
offset = SExtInst(offset, LLVMTypes::Int64VectorType, "offset_to_64");
return BinaryOperator(llvm::Instruction::Add, basePtr, offset,
"struct_ptr_offset");
// Finally, if had a slice pointer going in, mash back together with
// the original (unchanged) slice offset.
if (baseIsSlicePtr)
return MakeSlicePointer(resultPtr, ExtractInst(fullBasePtr, 1));
else
return resultPtr;
}
@@ -2085,43 +2290,124 @@ FunctionEmitContext::LoadInst(llvm::Value *ptr, const char *name) {
}
/** Given a slice pointer to soa'd data that is a basic type (atomic,
pointer, or enum type), use the slice offset to compute pointer(s) to
the appropriate individual data element(s).
*/
static llvm::Value *
lFinalSliceOffset(FunctionEmitContext *ctx, llvm::Value *ptr,
const PointerType **ptrType) {
Assert(dynamic_cast<const PointerType *>(*ptrType) != NULL);
llvm::Value *slicePtr = ctx->ExtractInst(ptr, 0, "slice_ptr");
llvm::Value *sliceOffset = ctx->ExtractInst(ptr, 1, "slice_offset");
// slicePtr should be a pointer to an soa-width wide array of the
// final atomic/enum/pointer type
const Type *unifBaseType = (*ptrType)->GetBaseType()->GetAsUniformType();
Assert(Type::IsBasicType(unifBaseType));
// The final pointer type is a uniform or varying pointer to the
// underlying uniform type, depending on whether the given pointer is
// uniform or varying.
*ptrType = (*ptrType)->IsUniformType() ?
PointerType::GetUniform(unifBaseType) :
PointerType::GetVarying(unifBaseType);
// For uniform pointers, bitcast to a pointer to the uniform element
// type, so that the GEP below does the desired indexing
if ((*ptrType)->IsUniformType())
slicePtr = ctx->BitCastInst(slicePtr, (*ptrType)->LLVMType(g->ctx));
// And finally index based on the slice offset
return ctx->GetElementPtrInst(slicePtr, sliceOffset, *ptrType,
"final_slice_gep");
}
/** Utility routine that loads from a uniform pointer to soa<> data,
returning a regular uniform (non-SOA result).
*/
llvm::Value *
FunctionEmitContext::loadUniformFromSOA(llvm::Value *ptr, llvm::Value *mask,
const PointerType *ptrType,
const char *name) {
const Type *unifType = ptrType->GetBaseType()->GetAsUniformType();
const CollectionType *ct =
dynamic_cast<const CollectionType *>(ptrType->GetBaseType());
if (ct != NULL) {
// If we have a struct/array, we need to decompose it into
// individual element loads to fill in the result structure since
// the SOA slice of values we need isn't contiguous in memory...
LLVM_TYPE_CONST llvm::Type *llvmReturnType = unifType->LLVMType(g->ctx);
llvm::Value *retValue = llvm::UndefValue::get(llvmReturnType);
for (int i = 0; i < ct->GetElementCount(); ++i) {
const PointerType *eltPtrType;
llvm::Value *eltPtr = AddElementOffset(ptr, i, ptrType,
"elt_offset", &eltPtrType);
llvm::Value *eltValue = LoadInst(eltPtr, mask, eltPtrType, name);
retValue = InsertInst(retValue, eltValue, i, "set_value");
}
return retValue;
}
else {
// Otherwise we've made our way to a slice pointer to a basic type;
// we need to apply the slice offset into this terminal SOA array
// and then perform the final load
ptr = lFinalSliceOffset(this, ptr, &ptrType);
return LoadInst(ptr, mask, ptrType, name);
}
}
llvm::Value *
FunctionEmitContext::LoadInst(llvm::Value *ptr, llvm::Value *mask,
const Type *ptrType, const char *name) {
const Type *ptrRefType, const char *name) {
if (ptr == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
Assert(ptrType != NULL && mask != NULL);
Assert(ptrRefType != NULL && mask != NULL);
if (dynamic_cast<const ReferenceType *>(ptrType) != NULL)
ptrType = PointerType::GetUniform(ptrType->GetReferenceTarget());
Assert(dynamic_cast<const PointerType *>(ptrType) != NULL);
const PointerType *ptrType;
if (dynamic_cast<const ReferenceType *>(ptrRefType) != NULL)
ptrType = PointerType::GetUniform(ptrRefType->GetReferenceTarget());
else {
ptrType = dynamic_cast<const PointerType *>(ptrRefType);
Assert(ptrType != NULL);
}
if (ptrType->IsUniformType()) {
// FIXME: same issue as above load inst regarding alignment...
//
// If the ptr is a straight up regular pointer, then just issue
// a regular load. First figure out the alignment; in general we
// can just assume the natural alignment (0 here), but for varying
// atomic types, we need to make sure that the compiler emits
// unaligned vector loads, so we specify a reduced alignment here.
int align = 0;
const AtomicType *atomicType =
dynamic_cast<const AtomicType *>(ptrType->GetBaseType());
if (atomicType != NULL && atomicType->IsVaryingType())
// We actually just want to align to the vector element
// alignment, but can't easily get that here, so just tell LLVM
// it's totally unaligned. (This shouldn't make any difference
// vs the proper alignment in practice.)
align = 1;
llvm::Instruction *inst = new llvm::LoadInst(ptr, name ? name : "load",
false /* not volatile */,
align, bblock);
AddDebugPos(inst);
return inst;
if (ptrType->IsSlice()) {
return loadUniformFromSOA(ptr, mask, ptrType, name);
}
else {
// FIXME: same issue as above load inst regarding alignment...
//
// If the ptr is a straight up regular pointer, then just issue
// a regular load. First figure out the alignment; in general we
// can just assume the natural alignment (0 here), but for varying
// atomic types, we need to make sure that the compiler emits
// unaligned vector loads, so we specify a reduced alignment here.
int align = 0;
const AtomicType *atomicType =
dynamic_cast<const AtomicType *>(ptrType->GetBaseType());
if (atomicType != NULL && atomicType->IsVaryingType())
// We actually just want to align to the vector element
// alignment, but can't easily get that here, so just tell LLVM
// it's totally unaligned. (This shouldn't make any difference
// vs the proper alignment in practice.)
align = 1;
llvm::Instruction *inst = new llvm::LoadInst(ptr, name ? name : "load",
false /* not volatile */,
align, bblock);
AddDebugPos(inst);
return inst;
}
}
else {
// Otherwise we should have a varying ptr and it's time for a
@@ -2132,11 +2418,10 @@ FunctionEmitContext::LoadInst(llvm::Value *ptr, llvm::Value *mask,
llvm::Value *
FunctionEmitContext::gather(llvm::Value *ptr, const Type *ptrType,
FunctionEmitContext::gather(llvm::Value *ptr, const PointerType *ptrType,
llvm::Value *mask, const char *name) {
// We should have a varying lvalue if we get here...
Assert(ptrType->IsVaryingType() &&
ptr->getType() == LLVMTypes::VoidPointerVectorType);
// We should have a varying pointer if we get here...
Assert(ptrType->IsVaryingType());
const Type *returnType = ptrType->GetBaseType()->GetAsVaryingType();
LLVM_TYPE_CONST llvm::Type *llvmReturnType = returnType->LLVMType(g->ctx);
@@ -2147,10 +2432,12 @@ FunctionEmitContext::gather(llvm::Value *ptr, const Type *ptrType,
// For collections, recursively gather element wise to find the
// result.
llvm::Value *retValue = llvm::UndefValue::get(llvmReturnType);
for (int i = 0; i < collectionType->GetElementCount(); ++i) {
llvm::Value *eltPtr = AddElementOffset(ptr, i, ptrType);
const Type *eltPtrType =
PointerType::GetVarying(collectionType->GetElementType(i));
const PointerType *eltPtrType;
llvm::Value *eltPtr =
AddElementOffset(ptr, i, ptrType, "gather_elt_ptr", &eltPtrType);
eltPtr = addVaryingOffsetsIfNeeded(eltPtr, eltPtrType);
// This in turn will be another gather
@@ -2160,7 +2447,15 @@ FunctionEmitContext::gather(llvm::Value *ptr, const Type *ptrType,
}
return retValue;
}
else if (ptrType->IsSlice()) {
// If we have a slice pointer, we need to add the final slice
// offset here right before issuing the actual gather
//
// FIXME: would it be better to do the corresponding same thing for
// all of the varying offsets stuff here (and in scatter)?
ptr = lFinalSliceOffset(this, ptr, &ptrType);
}
// Otherwise we should just have a basic scalar or pointer type and we
// can go and do the actual gather
AddInstrumentationPoint("gather");
@@ -2303,29 +2598,41 @@ FunctionEmitContext::maskedStore(llvm::Value *value, llvm::Value *ptr,
llvm::Value *eltValue = ExtractInst(value, i, "value_member");
llvm::Value *eltPtr =
AddElementOffset(ptr, i, ptrType, "struct_ptr_ptr");
const Type *eltPtrType =
PointerType::GetUniform(collectionType->GetElementType(i));
StoreInst(eltValue, eltPtr, mask, eltPtrType);
const Type *eltType = collectionType->GetElementType(i);
const Type *eltPtrType = PointerType::GetUniform(eltType);
StoreInst(eltValue, eltPtr, mask, eltType, eltPtrType);
}
return;
}
// We must have a regular atomic, enumerator, or pointer type at this
// point.
Assert(dynamic_cast<const AtomicType *>(valueType) != NULL ||
dynamic_cast<const EnumType *>(valueType) != NULL ||
dynamic_cast<const PointerType *>(valueType) != NULL);
Assert(Type::IsBasicType(valueType));
valueType = valueType->GetAsNonConstType();
llvm::Function *maskedStoreFunc = NULL;
// Figure out if we need a 8, 16, 32 or 64-bit masked store.
if (dynamic_cast<const PointerType *>(valueType) != NULL) {
llvm::Function *maskedStoreFunc = NULL;
const PointerType *pt = dynamic_cast<const PointerType *>(valueType);
if (pt != NULL) {
if (pt->IsSlice()) {
// For masked stores of (varying) slice pointers to memory, we
// grab the equivalent StructType and make a recursive call to
// maskedStore, giving it that type for the pointer type; that
// in turn will lead to the base pointer and offset index being
// mask stored to memory..
const StructType *sliceStructType = pt->GetSliceStructType();
ptrType = PointerType::GetUniform(sliceStructType);
maskedStore(value, ptr, ptrType, mask);
return;
}
if (g->target.is32Bit)
maskedStoreFunc = m->module->getFunction("__pseudo_masked_store_32");
else
maskedStoreFunc = m->module->getFunction("__pseudo_masked_store_64");
}
else if (valueType == AtomicType::VaryingBool &&
else if (Type::Equal(valueType, AtomicType::VaryingBool) &&
g->target.maskBitCount == 1) {
llvm::Value *notMask = BinaryOperator(llvm::Instruction::Xor, mask,
LLVMMaskAllOn, "~mask");
@@ -2339,35 +2646,35 @@ FunctionEmitContext::maskedStore(llvm::Value *value, llvm::Value *ptr,
StoreInst(final, ptr);
return;
}
else if (valueType == AtomicType::VaryingDouble ||
valueType == AtomicType::VaryingInt64 ||
valueType == AtomicType::VaryingUInt64) {
else if (Type::Equal(valueType, AtomicType::VaryingDouble) ||
Type::Equal(valueType, AtomicType::VaryingInt64) ||
Type::Equal(valueType, AtomicType::VaryingUInt64)) {
maskedStoreFunc = m->module->getFunction("__pseudo_masked_store_64");
ptr = BitCastInst(ptr, LLVMTypes::Int64VectorPointerType,
"ptr_to_int64vecptr");
value = BitCastInst(value, LLVMTypes::Int64VectorType,
"value_to_int64");
}
else if (valueType == AtomicType::VaryingFloat ||
valueType == AtomicType::VaryingBool ||
valueType == AtomicType::VaryingInt32 ||
valueType == AtomicType::VaryingUInt32 ||
else if (Type::Equal(valueType, AtomicType::VaryingFloat) ||
Type::Equal(valueType, AtomicType::VaryingBool) ||
Type::Equal(valueType, AtomicType::VaryingInt32) ||
Type::Equal(valueType, AtomicType::VaryingUInt32) ||
dynamic_cast<const EnumType *>(valueType) != NULL) {
maskedStoreFunc = m->module->getFunction("__pseudo_masked_store_32");
ptr = BitCastInst(ptr, LLVMTypes::Int32VectorPointerType,
"ptr_to_int32vecptr");
if (valueType == AtomicType::VaryingFloat)
if (Type::Equal(valueType, AtomicType::VaryingFloat))
value = BitCastInst(value, LLVMTypes::Int32VectorType,
"value_to_int32");
}
else if (valueType == AtomicType::VaryingInt16 ||
valueType == AtomicType::VaryingUInt16) {
else if (Type::Equal(valueType, AtomicType::VaryingInt16) ||
Type::Equal(valueType, AtomicType::VaryingUInt16)) {
maskedStoreFunc = m->module->getFunction("__pseudo_masked_store_16");
ptr = BitCastInst(ptr, LLVMTypes::Int16VectorPointerType,
"ptr_to_int16vecptr");
}
else if (valueType == AtomicType::VaryingInt8 ||
valueType == AtomicType::VaryingUInt8) {
else if (Type::Equal(valueType, AtomicType::VaryingInt8) ||
Type::Equal(valueType, AtomicType::VaryingUInt8)) {
maskedStoreFunc = m->module->getFunction("__pseudo_masked_store_8");
ptr = BitCastInst(ptr, LLVMTypes::Int8VectorPointerType,
"ptr_to_int8vecptr");
@@ -2391,28 +2698,68 @@ FunctionEmitContext::maskedStore(llvm::Value *value, llvm::Value *ptr,
*/
void
FunctionEmitContext::scatter(llvm::Value *value, llvm::Value *ptr,
const Type *ptrType, llvm::Value *mask) {
Assert(dynamic_cast<const PointerType *>(ptrType) != NULL);
const Type *valueType, const Type *origPt,
llvm::Value *mask) {
const PointerType *ptrType = dynamic_cast<const PointerType *>(origPt);
Assert(ptrType != NULL);
Assert(ptrType->IsVaryingType());
const Type *valueType = ptrType->GetBaseType();
// I think this should be impossible
Assert(dynamic_cast<const ArrayType *>(valueType) == NULL);
const CollectionType *collectionType = dynamic_cast<const CollectionType *>(valueType);
if (collectionType != NULL) {
const CollectionType *srcCollectionType =
dynamic_cast<const CollectionType *>(valueType);
if (srcCollectionType != NULL) {
// We're scattering a collection type--we need to keep track of the
// source type (the type of the data values to be stored) and the
// destination type (the type of objects in memory that will be
// stored into) separately. This is necessary so that we can get
// all of the addressing calculations right if we're scattering
// from a varying struct to an array of uniform instances of the
// same struct type, versus scattering into an array of varying
// instances of the struct type, etc.
const CollectionType *dstCollectionType =
dynamic_cast<const CollectionType *>(ptrType->GetBaseType());
Assert(dstCollectionType != NULL);
// Scatter the collection elements individually
for (int i = 0; i < collectionType->GetElementCount(); ++i) {
llvm::Value *eltPtr = AddElementOffset(ptr, i, ptrType);
for (int i = 0; i < srcCollectionType->GetElementCount(); ++i) {
// First, get the values for the current element out of the
// source.
llvm::Value *eltValue = ExtractInst(value, i);
const Type *eltPtrType =
PointerType::GetVarying(collectionType->GetElementType(i));
eltPtr = addVaryingOffsetsIfNeeded(eltPtr, eltPtrType);
scatter(eltValue, eltPtr, eltPtrType, mask);
const Type *srcEltType = srcCollectionType->GetElementType(i);
// We may be scattering a uniform atomic element; in this case
// we'll smear it out to be varying before making the recursive
// scatter() call below.
if (srcEltType->IsUniformType() && Type::IsBasicType(srcEltType)) {
eltValue = SmearUniform(eltValue, "to_varying");
srcEltType = srcEltType->GetAsVaryingType();
}
// Get the (varying) pointer to the i'th element of the target
// collection
llvm::Value *eltPtr = AddElementOffset(ptr, i, ptrType);
// The destination element type may be uniform (e.g. if we're
// scattering to an array of uniform structs). Thus, we need
// to be careful about passing the correct type to
// addVaryingOffsetsIfNeeded() here.
const Type *dstEltType = dstCollectionType->GetElementType(i);
const PointerType *dstEltPtrType = PointerType::GetVarying(dstEltType);
if (ptrType->IsSlice())
dstEltPtrType = dstEltPtrType->GetAsSlice();
eltPtr = addVaryingOffsetsIfNeeded(eltPtr, dstEltPtrType);
// And recursively scatter() until we hit a basic type, at
// which point the actual memory operations can be performed...
scatter(eltValue, eltPtr, srcEltType, dstEltPtrType, mask);
}
return;
}
else if (ptrType->IsSlice()) {
// As with gather, we need to add the final slice offset finally
// once we get to a terminal SOA array of basic types..
ptr = lFinalSliceOffset(this, ptr, &ptrType);
}
const PointerType *pt = dynamic_cast<const PointerType *>(valueType);
@@ -2483,19 +2830,28 @@ FunctionEmitContext::StoreInst(llvm::Value *value, llvm::Value *ptr) {
void
FunctionEmitContext::StoreInst(llvm::Value *value, llvm::Value *ptr,
llvm::Value *mask, const Type *ptrType) {
llvm::Value *mask, const Type *valueType,
const Type *ptrRefType) {
if (value == NULL || ptr == NULL) {
// may happen due to error elsewhere
Assert(m->errorCount > 0);
return;
}
if (dynamic_cast<const ReferenceType *>(ptrType) != NULL)
ptrType = PointerType::GetUniform(ptrType->GetReferenceTarget());
const PointerType *ptrType;
if (dynamic_cast<const ReferenceType *>(ptrRefType) != NULL)
ptrType = PointerType::GetUniform(ptrRefType->GetReferenceTarget());
else {
ptrType = dynamic_cast<const PointerType *>(ptrRefType);
Assert(ptrType != NULL);
}
// Figure out what kind of store we're doing here
if (ptrType->IsUniformType()) {
if (ptrType->GetBaseType()->IsUniformType())
if (ptrType->IsSlice())
// storing a uniform value to a single slice of a SOA type
storeUniformToSOA(value, ptr, mask, valueType, ptrType);
else if (ptrType->GetBaseType()->IsUniformType())
// the easy case
StoreInst(value, ptr);
else if (mask == LLVMMaskAllOn && !g->opt.disableMaskAllOnOptimizations)
@@ -2509,11 +2865,72 @@ FunctionEmitContext::StoreInst(llvm::Value *value, llvm::Value *ptr,
Assert(ptrType->IsVaryingType());
// We have a varying ptr (an array of pointers), so it's time to
// scatter
scatter(value, ptr, ptrType, GetFullMask());
scatter(value, ptr, valueType, ptrType, GetFullMask());
}
}
/** Store a uniform type to SOA-laid-out memory.
*/
void
FunctionEmitContext::storeUniformToSOA(llvm::Value *value, llvm::Value *ptr,
llvm::Value *mask, const Type *valueType,
const PointerType *ptrType) {
Assert(Type::Equal(ptrType->GetBaseType()->GetAsUniformType(), valueType));
const CollectionType *ct = dynamic_cast<const CollectionType *>(valueType);
if (ct != NULL) {
// Handle collections element wise...
for (int i = 0; i < ct->GetElementCount(); ++i) {
llvm::Value *eltValue = ExtractInst(value, i);
const Type *eltType = ct->GetElementType(i);
const PointerType *dstEltPtrType;
llvm::Value *dstEltPtr =
AddElementOffset(ptr, i, ptrType, "slice_offset",
&dstEltPtrType);
StoreInst(eltValue, dstEltPtr, mask, eltType, dstEltPtrType);
}
}
else {
// We're finally at a leaf SOA array; apply the slice offset and
// then we can do a final regular store
Assert(Type::IsBasicType(valueType));
ptr = lFinalSliceOffset(this, ptr, &ptrType);
StoreInst(value, ptr);
}
}
void
FunctionEmitContext::MemcpyInst(llvm::Value *dest, llvm::Value *src,
llvm::Value *count, llvm::Value *align) {
dest = BitCastInst(dest, LLVMTypes::VoidPointerType);
src = BitCastInst(src, LLVMTypes::VoidPointerType);
if (count->getType() != LLVMTypes::Int64Type) {
Assert(count->getType() == LLVMTypes::Int32Type);
count = ZExtInst(count, LLVMTypes::Int64Type, "count_to_64");
}
if (align == NULL)
align = LLVMInt32(1);
llvm::Constant *mcFunc =
m->module->getOrInsertFunction("llvm.memcpy.p0i8.p0i8.i64",
LLVMTypes::VoidType, LLVMTypes::VoidPointerType,
LLVMTypes::VoidPointerType, LLVMTypes::Int64Type,
LLVMTypes::Int32Type, LLVMTypes::BoolType, NULL);
Assert(mcFunc != NULL);
Assert(llvm::isa<llvm::Function>(mcFunc));
std::vector<llvm::Value *> args;
args.push_back(dest);
args.push_back(src);
args.push_back(count);
args.push_back(align);
args.push_back(LLVMFalse); /* not volatile */
CallInst(mcFunc, NULL, args, "");
}
void
FunctionEmitContext::BranchInst(llvm::BasicBlock *dest) {
llvm::Instruction *b = llvm::BranchInst::Create(dest, bblock);
@@ -2761,7 +3178,7 @@ FunctionEmitContext::CallInst(llvm::Value *func, const FunctionType *funcType,
// accumulate the result using the call mask.
if (callResult != NULL) {
Assert(resultPtr != NULL);
StoreInst(callResult, resultPtr, callMask,
StoreInst(callResult, resultPtr, callMask, returnType,
PointerType::GetUniform(returnType));
}
else
@@ -2825,7 +3242,7 @@ FunctionEmitContext::ReturnInst() {
rinst = llvm::ReturnInst::Create(*g->ctx, retVal, bblock);
}
else {
Assert(function->GetReturnType() == AtomicType::Void);
Assert(Type::Equal(function->GetReturnType(), AtomicType::Void));
rinst = llvm::ReturnInst::Create(*g->ctx, bblock);
}
@@ -2945,11 +3362,10 @@ FunctionEmitContext::addVaryingOffsetsIfNeeded(llvm::Value *ptr,
Assert(pt && pt->IsVaryingType());
const Type *baseType = ptrType->GetBaseType();
if (dynamic_cast<const AtomicType *>(baseType) == NULL &&
dynamic_cast<const EnumType *>(baseType) == NULL &&
dynamic_cast<const PointerType *>(baseType) == NULL)
if (Type::IsBasicType(baseType) == false)
return ptr;
if (baseType->IsUniformType())
if (baseType->IsVaryingType() == false)
return ptr;
// Find the size of a uniform element of the varying type

39
ctx.h
View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2011, Intel Corporation
Copyright (c) 2010-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -392,6 +392,16 @@ public:
llvm::Instruction *ZExtInst(llvm::Value *value, LLVM_TYPE_CONST llvm::Type *type,
const char *name = NULL);
/** Given two integer-typed values (but possibly one vector and the
other not, and or of possibly-different bit-widths), update their
values as needed so that the two have the same (more general)
type. */
void MatchIntegerTypes(llvm::Value **v0, llvm::Value **v1);
/** Create a new slice pointer out of the given pointer to an soa type
and an integer offset to a slice within that type. */
llvm::Value *MakeSlicePointer(llvm::Value *ptr, llvm::Value *offset);
/** These GEP methods are generalizations of the standard ones in LLVM;
they support both uniform and varying basePtr values as well as
uniform and varying index values (arrays of indices). Varying base
@@ -412,7 +422,8 @@ public:
the type of the pointer, though it may be NULL if the base pointer
is uniform. */
llvm::Value *AddElementOffset(llvm::Value *basePtr, int elementNum,
const Type *ptrType, const char *name = NULL);
const Type *ptrType, const char *name = NULL,
const PointerType **resultPtrType = NULL);
/** Load from the memory location(s) given by lvalue, using the given
mask. The lvalue may be varying, in which case this corresponds to
@@ -443,7 +454,14 @@ public:
varying, the given storeMask is used to mask the stores so that
they only execute for the active program instances. */
void StoreInst(llvm::Value *value, llvm::Value *ptr,
llvm::Value *storeMask, const Type *ptrType);
llvm::Value *storeMask, const Type *valueType,
const Type *ptrType);
/** Copy count bytes of memory from the location pointed to by src to
the location pointed to by dest. (src and dest must not be
overlapping.) */
void MemcpyInst(llvm::Value *dest, llvm::Value *src, llvm::Value *count,
llvm::Value *align = NULL);
void BranchInst(llvm::BasicBlock *block);
void BranchInst(llvm::BasicBlock *trueBlock, llvm::BasicBlock *falseBlock,
@@ -646,12 +664,19 @@ private:
CFInfo *popCFState();
void scatter(llvm::Value *value, llvm::Value *ptr, const Type *ptrType,
llvm::Value *mask);
void scatter(llvm::Value *value, llvm::Value *ptr, const Type *valueType,
const Type *ptrType, llvm::Value *mask);
void maskedStore(llvm::Value *value, llvm::Value *ptr, const Type *ptrType,
llvm::Value *mask);
llvm::Value *gather(llvm::Value *ptr, const Type *ptrType, llvm::Value *mask,
const char *name);
void storeUniformToSOA(llvm::Value *value, llvm::Value *ptr,
llvm::Value *mask, const Type *valueType,
const PointerType *ptrType);
llvm::Value *loadUniformFromSOA(llvm::Value *ptr, llvm::Value *mask,
const PointerType *ptrType, const char *name);
llvm::Value *gather(llvm::Value *ptr, const PointerType *ptrType,
llvm::Value *mask, const char *name);
llvm::Value *addVaryingOffsetsIfNeeded(llvm::Value *ptr, const Type *ptrType);
};

136
decl.cpp
View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2011, Intel Corporation
Copyright (c) 2010-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -69,12 +69,21 @@ lApplyTypeQualifiers(int typeQualifiers, const Type *type, SourcePos pos) {
if ((typeQualifiers & TYPEQUAL_CONST) != 0)
type = type->GetAsConstType();
if ((typeQualifiers & TYPEQUAL_UNIFORM) != 0)
type = type->GetAsUniformType();
else if ((typeQualifiers & TYPEQUAL_VARYING) != 0)
type = type->GetAsVaryingType();
if ((typeQualifiers & TYPEQUAL_UNIFORM) != 0) {
if (Type::Equal(type, AtomicType::Void))
Error(pos, "\"uniform\" qualifier is illegal with \"void\" type.");
else
type = type->GetAsUniformType();
}
else if ((typeQualifiers & TYPEQUAL_VARYING) != 0) {
if (Type::Equal(type, AtomicType::Void))
Error(pos, "\"varying\" qualifier is illegal with \"void\" type.");
else
type = type->GetAsVaryingType();
}
else
type = type->GetAsUnboundVariabilityType();
if (Type::Equal(type, AtomicType::Void) == false)
type = type->GetAsUnboundVariabilityType();
if ((typeQualifiers & TYPEQUAL_UNSIGNED) != 0) {
if ((typeQualifiers & TYPEQUAL_SIGNED) != 0)
@@ -84,15 +93,20 @@ lApplyTypeQualifiers(int typeQualifiers, const Type *type, SourcePos pos) {
const Type *unsignedType = type->GetAsUnsignedType();
if (unsignedType != NULL)
type = unsignedType;
else
else {
const Type *resolvedType =
type->ResolveUnboundVariability(Variability::Varying);
Error(pos, "\"unsigned\" qualifier is illegal with \"%s\" type.",
type->ResolveUnboundVariability(Type::Varying)->GetString().c_str());
resolvedType->GetString().c_str());
}
}
if ((typeQualifiers & TYPEQUAL_SIGNED) != 0 && type->IsIntType() == false)
if ((typeQualifiers & TYPEQUAL_SIGNED) != 0 && type->IsIntType() == false) {
const Type *resolvedType =
type->ResolveUnboundVariability(Variability::Varying);
Error(pos, "\"signed\" qualifier is illegal with non-integer type "
"\"%s\".",
type->ResolveUnboundVariability(Type::Varying)->GetString().c_str());
"\"%s\".", resolvedType->GetString().c_str());
}
return type;
}
@@ -112,24 +126,54 @@ DeclSpecs::DeclSpecs(const Type *t, StorageClass sc, int tq) {
const Type *
DeclSpecs::GetBaseType(SourcePos pos) const {
const Type *bt = baseType;
const Type *retType = baseType;
if (bt == NULL) {
if (retType == NULL) {
Warning(pos, "No type specified in declaration. Assuming int32.");
bt = AtomicType::UnboundInt32;
retType = AtomicType::UniformInt32->GetAsUnboundVariabilityType();
}
if (vectorSize > 0) {
const AtomicType *atomicType = dynamic_cast<const AtomicType *>(bt);
const AtomicType *atomicType = dynamic_cast<const AtomicType *>(retType);
if (atomicType == NULL) {
Error(pos, "Only atomic types (int, float, ...) are legal for vector "
"types.");
return NULL;
}
bt = new VectorType(atomicType, vectorSize);
retType = new VectorType(atomicType, vectorSize);
}
return lApplyTypeQualifiers(typeQualifiers, bt, pos);
retType = lApplyTypeQualifiers(typeQualifiers, retType, pos);
if (soaWidth > 0) {
const StructType *st = dynamic_cast<const StructType *>(retType);
if (st == NULL) {
Error(pos, "Illegal to provide soa<%d> qualifier with non-struct "
"type \"%s\".", soaWidth, retType->GetString().c_str());
return NULL;
}
else if (soaWidth <= 0 || (soaWidth & (soaWidth - 1)) != 0) {
Error(pos, "soa<%d> width illegal. Value must be positive power "
"of two.", soaWidth);
return NULL;
}
if (st->IsUniformType()) {
Error(pos, "\"uniform\" qualifier and \"soa<%d>\" qualifier can't "
"both be used in a type declaration.", soaWidth);
return NULL;
}
else if (st->IsVaryingType()) {
Error(pos, "\"varying\" qualifier and \"soa<%d>\" qualifier can't "
"both be used in a type declaration.", soaWidth);
return NULL;
}
else
retType = st->GetAsSOAType(soaWidth);
}
return retType;
}
@@ -280,13 +324,13 @@ Declarator::GetFunctionInfo(DeclSpecs *ds, std::vector<Symbol *> *funArgs) {
continue;
}
else
sym->type = sym->type->ResolveUnboundVariability(Type::Varying);
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
funArgs->push_back(sym);
}
if (funSym != NULL)
funSym->type = funSym->type->ResolveUnboundVariability(Type::Varying);
funSym->type = funSym->type->ResolveUnboundVariability(Variability::Varying);
return funSym;
}
@@ -306,11 +350,11 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
if (kind != DK_FUNCTION && isTask)
Error(pos, "\"task\" qualifier illegal in variable declaration.");
Type::Variability variability = Type::Unbound;
Variability variability(Variability::Unbound);
if (hasUniformQual)
variability = Type::Uniform;
variability = Variability::Uniform;
else if (hasVaryingQual)
variability = Type::Varying;
variability = Variability::Varying;
const Type *type = base;
switch (kind) {
@@ -322,7 +366,10 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
return type;
case DK_POINTER:
type = new PointerType(type, variability, isConst);
/* For now, any pointer to an SOA type gets the slice property; if
we add the capability to declare pointers as slices or not,
we'll want to set this based on a type qualifier here. */
type = new PointerType(type, variability, isConst, type->IsSOAType());
if (child != NULL)
return child->GetType(type, ds);
else
@@ -351,7 +398,7 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
break;
case DK_ARRAY:
if (type == AtomicType::Void) {
if (Type::Equal(type, AtomicType::Void)) {
Error(pos, "Arrays of \"void\" type are illegal.");
return NULL;
}
@@ -387,7 +434,7 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
"function parameter declaration for parameter \"%s\".",
lGetStorageClassName(d->declSpecs->storageClass),
sym->name.c_str());
if (sym->type == AtomicType::Void) {
if (Type::Equal(sym->type, AtomicType::Void)) {
Error(sym->pos, "Parameter with type \"void\" illegal in function "
"parameter list.");
sym->type = NULL;
@@ -408,7 +455,11 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
return NULL;
}
sym->type = PointerType::GetUniform(at->GetElementType());
const Type *targetType = at->GetElementType();
targetType =
targetType->ResolveUnboundVariability(Variability::Varying);
sym->type = PointerType::GetUniform(targetType);
// Make sure there are no unsized arrays (other than the
// first dimension) in function parameter lists.
at = dynamic_cast<const ArrayType *>(at->GetElementType());
@@ -485,36 +536,13 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
const Type *functionType =
new FunctionType(returnType, args, argNames, argDefaults,
argPos, isTask, isExported, isExternC);
functionType = functionType->ResolveUnboundVariability(Type::Varying);
functionType = functionType->ResolveUnboundVariability(Variability::Varying);
return child->GetType(functionType, ds);
}
default:
FATAL("Unexpected decl kind");
return NULL;
}
#if 0
// Make sure we actually have an array of structs ..
const StructType *childStructType =
dynamic_cast<const StructType *>(childType);
if (childStructType == NULL) {
Error(pos, "Illegal to provide soa<%d> qualifier with non-struct "
"type \"%s\".", soaWidth, childType->GetString().c_str());
return new ArrayType(childType, arraySize == -1 ? 0 : arraySize);
}
else if ((soaWidth & (soaWidth - 1)) != 0) {
Error(pos, "soa<%d> width illegal. Value must be power of two.",
soaWidth);
return NULL;
}
else if (arraySize != -1 && (arraySize % soaWidth) != 0) {
Error(pos, "soa<%d> width must evenly divide array size %d.",
soaWidth, arraySize);
return NULL;
}
return new SOAArrayType(childStructType, arraySize == -1 ? 0 : arraySize,
soaWidth);
#endif
}
@@ -596,9 +624,9 @@ Declaration::GetVariableDeclarations() const {
Assert(m->errorCount > 0);
continue;
}
sym->type = sym->type->ResolveUnboundVariability(Type::Varying);
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
if (sym->type == AtomicType::Void)
if (Type::Equal(sym->type, AtomicType::Void))
Error(sym->pos, "\"void\" type variable illegal in declaration.");
else if (dynamic_cast<const FunctionType *>(sym->type) == NULL) {
m->symbolTable->AddVariable(sym);
@@ -627,7 +655,7 @@ Declaration::DeclareFunctions() {
Assert(m->errorCount > 0);
continue;
}
sym->type = sym->type->ResolveUnboundVariability(Type::Varying);
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
if (dynamic_cast<const FunctionType *>(sym->type) == NULL)
continue;
@@ -674,7 +702,7 @@ GetStructTypesNamesPositions(const std::vector<StructDeclaration *> &sd,
Symbol *sym = d->GetSymbol();
if (sym->type == AtomicType::Void)
if (Type::Equal(sym->type, AtomicType::Void))
Error(d->pos, "\"void\" type illegal for struct member.");
const ArrayType *arrayType =

View File

@@ -5,6 +5,9 @@ for i in ispc perfguide faq; do
--stylesheet-path=css/style.css $i.rst > $i.html
done
rst2html.py --template=template-news.txt --link-stylesheet \
--stylesheet-path=css/style.css news.rst > news.html
rst2html.py --template=template-perf.txt --link-stylesheet \
--stylesheet-path=css/style.css perf.rst > perf.html

View File

@@ -19,6 +19,7 @@ distribution.
+ `How can I supply an initial execution mask in the call from the application?`_
+ `How can I generate a single binary executable with support for multiple instruction sets?`_
+ `How can I determine at run-time which vector instruction set's instructions were selected to execute?`_
+ `Is it possible to inline ispc functions in C/C++ code?`_
* Programming Techniques
@@ -346,6 +347,50 @@ In a similar fashion, it's possible to find out at run-time the value of
export uniform int width() { return programCount; }
Is it possible to inline ispc functions in C/C++ code?
------------------------------------------------------
If you're willing to use the ``clang`` C/C++ compiler that's part of the
LLVM tool suite, then it is possible to inline ``ispc`` code with C/C++
(and conversely, to inline C/C++ calls in ``ispc``). Doing so can provide
performance advantages when calling out to short functions written in the
"other" language. Note that you don't need to use ``clang`` to compile all
of your C/C++ code, but only for the files where you want to be able to
inline. In order to do this, you must have a full installation of LLVM
version 3.0 or later, including the ``clang`` compiler.
The basic approach is to have the various compilers emit LLVM intermediate
representation (IR) code and to then use tools from LLVM to link together
the IR from the compilers and then re-optimize it, which gives the LLVM
optimizer the opportunity to do additional inlining and cross-function
optimizations. If you have source files ``foo.ispc`` and ``foo.cpp``,
first emit LLVM IR:
::
ispc --emit-llvm -o foo_ispc.bc foo.ispc
clang -O2 -c -emit-llvm -o foo_cpp.bc foo.cpp
Next, link the two IR files into a single file and run the LLVM optimizer
on the result:
::
llvm-link foo_ispc.bc foo_cpp.bc -o - | opt -O3 -o foo_opt.bc
And finally, generate a native object file:
::
llc -filetype=obj foo_opt.bc -o foo.o
This file can in turn be linked in with the rest of your object files when
linking your applicaiton.
(Note that if you're using the AVX instruction set, you must provide the
``-mattr=+avx`` flag to ``llc``.)
Programming Techniques
======================

View File

@@ -92,7 +92,9 @@ Contents:
* `Reference Types`_
* `Enumeration Types`_
* `Short Vector Types`_
* `Struct and Array Types`_
* `Array Types`_
* `Struct Types`_
* `Structure of Array Types`_
+ `Declarations and Initializers`_
+ `Expressions`_
@@ -132,9 +134,13 @@ Contents:
* `Reductions`_
+ `Data Conversions And Storage`_
+ `Data Movement`_
* `Setting and Copying Values In Memory`_
* `Packed Load and Store Operations`_
+ `Data Conversions`_
* `Converting Between Array-of-Structures and Structure-of-Arrays Layout`_
* `Conversions To and From Half-Precision Floats`_
@@ -150,7 +156,6 @@ Contents:
+ `Data Layout`_
+ `Data Alignment and Aliasing`_
+ `Restructuring Existing Programs to Use ISPC`_
+ `Understanding How to Interoperate With the Application's Data`_
* `Disclaimer and Legal Information`_
@@ -859,7 +864,9 @@ A variable that is declared with the ``uniform`` qualifier represents a
single value that is shared across the entire gang. (In contrast, the
default variability qualifier for variables in ``ispc``, ``varying``,
represents a variable that has a distinct storage location for each program
instance in the gang.)
instance in the gang.) (Though see the discussion in `Struct Types`_ for
some subtleties related to ``uniform`` and ``varying`` when used with
structures.)
It is an error to try to assign a ``varying`` value to a ``uniform``
variable, though ``uniform`` values can be assigned to ``uniform``
@@ -1499,56 +1506,72 @@ Pointer Types
It is possible to have pointers to data in memory; pointer arithmetic,
changing values in memory with pointers, and so forth is supported as in C.
As with other basic types, pointers can be both ``uniform`` and
``varying``.
** Like other types in ``ispc``, pointers are ``varying`` by default, if an
explicit ``uniform`` qualifier isn't provided. However, the default
variability of the pointed-to type is uniform. ** This rule will be
illustrated and explained in examples below.
For example, the ``ptr`` variable in the code below is a varying pointer to
``uniform float`` values. Each program instance has a separate pointer
value and the assignment to ``*ptr`` generally represents a scatter to
memory.
::
float a = 0;
float *pa = &a;
*pa = 1; // now a == 1
uniform float a[] = ...;
int index = ...;
float * ptr = &a[index];
*ptr = 1;
A ``uniform`` pointer can be declared with an appropriately-placed
qualifier:
::
float f = 0;
varying float * uniform pf = &f; // uniform pointer to a varying float
*pf = 1;
The placement of the ``uniform`` qualifier to declare a ``uniform`` pointer
may be initially surprising, but it matches the form of how, for example, a
pointer that is itself ``const`` (as opposed to pointing to a ``const``
type) is declared in C. (Reading the declaration from right to left gives
its meaning: a uniform pointer to a float that is varying.)
A subtlety comes in in cases like the where a uniform pointer points to a
varying datatype. In this case, each program instance accesses a distinct
location in memory (because the underlying varying datatype is itself laid
out with a separate location in memory for each program instance.)
::
float a;
varying float * uniform pa = &a;
*pa = programIndex; // same as (a = programIndex)
Also as in C, arrays are silently converted into pointers:
::
float a[10] = { ... };
float *pa = a; // pointer to first element of a
float *pb = a + 5; // pointer to 5th element of a
As with other basic types, pointers can be both ``uniform`` and
``varying``. By default, they are varying. The placement of the
``uniform`` qualifier to declare a ``uniform`` pointer may be initially
surprising, but it matches the form of how for example a pointer that is
itself ``const`` (as opposed to pointing to a ``const`` type) is declared
in C.
::
uniform float f = 0;
uniform float * uniform pf = &f;
*pf = 1;
A subtlety comes in when a uniform pointer points to a varying datatype.
In this case, each program instance accesses a distinct location in memory
(because the underlying varying datatype is itself laid out with a separate
location in memory for each program instance.)
::
float a;
float * uniform pa = &a;
*pa = programIndex; // same as (a = programIndex)
varying float * uniform pa = a; // pointer to first element of a
varying float * uniform pb = a + 5; // pointer to 5th element of a
Any pointer type can be explicitly typecast to another pointer type, as
long as the source type isn't a ``varying`` pointer when the destination
type is a ``uniform`` pointer. Like other types, ``uniform`` pointers can
be typecast to be ``varying`` pointers, however.
type is a ``uniform`` pointer.
::
float *pa = ...;
int *pb = (int *)pa; // legal, but beware
Like other types, ``uniform`` pointers can be typecast to be ``varying``
pointers, however.
Any pointer type can be assigned to a ``void`` pointer without a type cast:
::
@@ -1815,20 +1838,19 @@ expressions:
int8<2> bar = ...;
foo.yz = bar; // Error: can't assign to left-hand side of expression
Struct and Array Types
----------------------
More complex data structures can be built using ``struct`` and arrays.
Array Types
-----------
Arrays of any type can be declared just as in C and C++:
::
struct Foo {
float time;
int flags[10];
};
float a[10];
uniform int * varying b[20];
Like in C, multidimensional arrays can be specified; the following declares
an array of 5 arrays of 15 floats.
Multidimensional arrays can be specified as arrays of arrays; the following
declares an array of 5 arrays of 15 floats.
::
@@ -1843,6 +1865,27 @@ that functions can be declared to take "unsized arrays" as parameters:
void foo(float array[], int length);
Finally, the name of an array will be automatically implicitly converted to
a uniform pointer to the array type if needed:
::
int a[10];
int * uniform ap = a;
Struct Types
------------
Aggregate data structures can be built using ``struct``.
::
struct Foo {
float time;
int flags[10];
};
As in C++, after a ``struct`` is declared, an instance can be created using
the ``struct``'s name:
@@ -1856,6 +1899,131 @@ Alternatively, ``struct`` can be used before the structure name:
struct Foo f;
Members in a structure declaration may each have ``uniform`` or ``varying``
qualifiers, or may have no rate qualifier, in which case their variability
is initially "unbound".
::
struct Bar {
uniform int a;
varying int b;
int c;
};
In the declaration above, the variability of ``c`` is unbound. The
variability of struct members that are unbound is resolved when a struct is
defined; if the ``struct`` is ``uniform``, then unbound members are
``uniform``, and if the ``struct`` is ``varying``, then unbound members are
varying.
::
Bar vb;
uniform Bar ub;
Here, ``b`` is a ``varying Bar`` (since ``varying`` is the default
variability). If ``Bar`` is defined as above, then ``vb.a`` is still a
``uniform int``, since its varaibility was bound in the original
declaration of the ``Bar`` type. Similarly, ``vb.b`` is ``varying``. The
variability fo ``vb.c`` is ``varying``, since ``vb`` is ``varying``.
(Similarly, ``ub.a`` is ``uniform``, ``ub.b`` is ``varying``, and ``ub.c``
is ``uniform``.)
In most cases, it's worthwhile to declare ``struct`` members with unbound
variability so that all have the same variability for both ``uniform`` and
``varying`` structs. In particular, if a ``struct`` has a member with
bound ``uniform`` type, it's not possible to index into an array of the
struct type with a ``varying`` index. Consider the following example:
::
struct Foo { uniform int a; };
uniform Foo f[...] = ...;
int index = ...;
Foo fv = f[index]; // ERROR
Here, the ``Foo`` type has a member with bound ``uniform`` variability.
Because ``index`` has a different value for each program instance in the
above code, the value of ``f[index]`` needs to be able to store a different
value of ``Foo::a`` for each program instance. However, a ``varying Foo``
still has only a single ``a`` member, since ``a`` was declared with
``uniform`` variability in the declaration of ``Foo``. Therefore, the
indexing operation in the last line results in an error.
Structure of Array Types
------------------------
If data can be laid out in memory so that the executing program instances
access it via loads and stores of contiguous sections of memory, overall
performance can be improved noticably. One way to improve this memory
access coherence is to lay out structures in "structure of arrays" (SOA)
format in memory; the benefits from SOA layout are discussed in more detail
in the `Use "Structure of Arrays" Layout When Possible`_ section in the
ispc Performance Guide.
.. _Use "Structure of Arrays" Layout When Possible: perf.html#use-structure-of-arrays-layout-when-possible
``ispc`` provides two key language-level capabilities for laying out and
accessing data in SOA format:
* An ``soa`` keyword that transforms a regular ``struct`` into an SOA version
of the struct.
* Array indexing syntax for SOA arrays that transparently handles SOA
indexing.
As an example, consider a simple struct declaration:
::
struct Point { float x, y, z; };
With the ``soa`` rate qualifier, an array of SOA variants of this structure
can be declared:
::
soa<8> Point pts[...];
The in-memory layout of the ``Point``s has had the SOA transformation
applied, such that there are 8 ``x`` values in memory followed by 8 ``y``
values, and so forth. Here is the effective declaration of ``soa<8>
Point``:
::
struct { uniform float x[8], y[8], z[8]; };
Given an array of SOA data, array indexing (and pointer arithmetic) is done
so that the appropriate values from the SOA array are accessed. For
example, given:
::
soa<8> Point pts[...];
uniform float x = pts[10].x;
The generated code effectively accesses the second 8-wide SOA structure and
then loads the third ``x`` value from it. In general, one can write the
same code to access arrays of SOA elements as one would write to access
them in AOS layout.
Note that it directly follows from SOA layout that the layout of a single
element of the array isn't contiguous in memory--``pts[1].x`` and
``pts[1].y`` are separated by 7 ``float`` values in the above example.
There are a few limitations to the current implementation of SOA types in
``ispc``; these may be relaxed in future releases:
* It's illegal to typecast to ``soa`` data to ``void`` pointers.
* Reference types are illegal in SOA structures
* All members of SOA structures must have no rate qualifiers--specifically,
it's illegal to have an explicitly-qualified ``uniform`` or ``varying``
member of a structure that has ``soa`` applied to it.
Declarations and Initializers
-----------------------------
@@ -1992,7 +2160,7 @@ based on C++'s ``new`` and ``delete`` operators:
::
int count = ...;
int *ptr = new uniform int[count];
int *ptr = new int[count];
// use ptr...
delete[] ptr;
@@ -2002,13 +2170,22 @@ that memory. Uses of ``new`` and ``delete`` in ``ispc`` programs are
serviced by corresponding calls the system C library's ``malloc()`` and
``free()`` functions.
Note that the rules for ``uniform`` and ``varying`` for ``new`` are
analogous to the corresponding rules are for pointers (as described in
`Pointer Types`_.) Specifically, if a specific rate qualifier isn't
provided with the ``new`` expression, then the default is that a "varying"
``new`` is performed, where each program instance performs a unique
allocation. The allocated type, in turn, is by default ``uniform`` for
``varying`` ``new`` expressions, and ``varying`` for ``uniform`` new
expressions.
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:
points to. However, that deletion happens on a per-program-instance basis.
In other words, consider the following code:
::
int *ptr = new uniform int[count];
int *ptr = new int[count];
// use ptr
if (count > 1000)
delete[] ptr;
@@ -2033,7 +2210,9 @@ gang of program instances. A ``new`` statement can be qualified with
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.
pointer. Recall that with a ``uniform`` ``new``, the default variability
of the allocated type is ``varying``, so the above code is allocating an
array of ten ``varying float`` values.
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
@@ -2043,7 +2222,7 @@ statement, which is an error: effectively
::
float *ptr = uniform new float[10];
varying float * ptr = uniform new float[10];
// use ptr...
delete ptr; // ERROR: varying pointer is deleted
@@ -2052,28 +2231,31 @@ 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:
of ``uniform`` or ``varying``, 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];
float * p3 = new float[10];
varying float * p4 = new varying 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.
of 10 ``uniform float`` values (40 bytes), the second is a single
allocation of 10 ``varying float`` values (8*4*10 = 320 bytes), the third
is 8 allocations of 10 ``uniform float`` values (8 allocations of 40 bytes
each), and the last performs 8 allocations of 320 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.
``varying float``, which is wasteful. (This in turn is partially why the
allocated type is uniform by default with both pointers and ``new``
statements.)
Although ``ispc`` doesn't support constructors or destructors like C++, it
is possible to provide initializer values with ``new`` statements:
@@ -2835,13 +3017,17 @@ architectures.
float tan(float x)
uniform float tan(uniform float x)
Arctangent functions are also available:
The corresponding inverse functions are also available:
::
float asin(float x)
uniform float asin(uniformfloat x)
float acos(float x)
uniform float acos(uniform float x)
float atan(float x)
float atan2(float x, float y)
uniform float atan(uniform float x)
float atan2(float x, float y)
uniform float atan2(uniform float x, uniform float y)
If both sine and cosine are needed, then the ``sincos()`` call computes
@@ -2850,7 +3036,7 @@ functions:
::
void sincos(float x, float * uniform s, float * uniform c)
void sincos(float x, varying float * uniform s, varying float * uniform c)
void sincos(uniform float x, uniform float * uniform s,
uniform float * uniform c)
@@ -2876,7 +3062,7 @@ normalized exponent as a power of two in the ``pw2`` parameter.
float ldexp(float x, int n)
uniform float ldexp(uniform float x, uniform int n)
float frexp(float x, int * uniform pw2)
float frexp(float x, varying int * uniform pw2)
uniform float frexp(uniform float x,
uniform int * uniform pw2)
@@ -2891,7 +3077,8 @@ library. State for the RNG is maintained in an instance of the
::
struct RNGState;
void seed_rng(RNGState * uniform state, uniform int seed)
void seed_rng(varying RNGState * uniform state, uniform int seed)
void seed_rng(uniform RNGState * uniform state, uniform int seed)
After the RNG is seeded, the ``random()`` function can be used to get a
pseudo-random ``unsigned int32`` value and the ``frandom()`` function can
@@ -2899,8 +3086,10 @@ be used to get a pseudo-random ``float`` value.
::
unsigned int32 random(RNGState * uniform state)
float frandom(RNGState * uniform state)
unsigned int32 random(varying RNGState * uniform state)
float frandom(varying RNGState * uniform state)
uniform unsigned int32 random(RNGState * uniform state)
uniform float frandom(uniform RNGState * uniform state)
Output Functions
----------------
@@ -3202,8 +3391,52 @@ program instances into a compact output buffer is `discussed in the FAQ`_.
.. _discussed in the FAQ: faq.html#how-can-a-gang-of-program-instances-generate-variable-amounts-of-output-efficiently
Data Conversions And Storage
----------------------------
Data Movement
-------------
Setting and Copying Values In Memory
------------------------------------
There are a few functions for copying blocks of memory and initializing
values in memory. Along the lines of the equivalently-named routines in
the C Standard libary, ``memcpy`` copies a given number of bytes starting
from a source location in memory to a destination locaiton, where the two
regions of memory are guaranteed by the caller to be non-overlapping.
Alternatively, ``memmove`` can be used to copy data if the buffers may
overlap.
::
void memcpy(void * uniform dst, void * uniform src, uniform int32 count)
void memmove(void * uniform dst, void * uniform src, uniform int32 count)
void memcpy(void * varying dst, void * varying src, int32 count)
void memmove(void * varying dst, void * varying src, int32 count)
Note that there are variants of these functions that take both ``uniform``
and ``varying`` pointers.
To initialize values in memory, the ``memset`` routine can be used. (It
also behaves like the function of the same name in the C Standard Library.)
It sets the given number of bytes of memory starting at the given location
to the value provided.
::
void memset(void * uniform ptr, uniform int8 val, uniform int32 count)
void memset(void * varying ptr, int8 val, int32 count)
There are also variants of all of these functions that take 64-bit values
for the number of bytes of memory to operate on:
::
void memcpy64(void * uniform dst, void * uniform src, uniform int64 count)
void memcpy64(void * varying dst, void * varying src, int64 count)
void memmove64(void * uniform dst, void * uniform src, uniform int64 count)
void memmove64(void * varying dst, void * varying src, int64 count)
void memset64(void * uniform ptr, uniform int8 val, uniform int64 count)
void memset64(void * varying ptr, int8 val, int64 count)
Packed Load and Store Operations
--------------------------------
@@ -3218,9 +3451,9 @@ variable. They return the total number of values loaded.
::
uniform int packed_load_active(uniform int * uniform base,
int * uniform val)
varying int * uniform val)
uniform int packed_load_active(uniform unsigned int * uniform base,
unsigned int * uniform val)
varying unsigned int * uniform val)
Similarly, the ``packed_store_active()`` functions store the ``val`` values
for each program instances that executed the ``packed_store_active()``
@@ -3262,14 +3495,18 @@ of four negative values, and initializes the first four elements of
indices where ``a[i]`` was less than zero.
Data Conversions
----------------
Converting Between Array-of-Structures and Structure-of-Arrays Layout
---------------------------------------------------------------------
Applications often lay data out in memory in "array of structures" form.
Though convenient in C/C++ code, this layout can make ``ispc`` programs
less efficient than they would be if the data was laid out in "structure of
arrays" form. (See the section `Understanding How to Interoperate With the
Application's Data`_ for extended discussion of this topic.)
arrays" form. (See the section `Use "Structure of Arrays" Layout When
Possible`_ in the performance guide for extended discussion of this topic.)
The standard library does provide a few functions that efficiently convert
between these two formats, for cases where it's not possible to change the
@@ -3309,10 +3546,10 @@ are both ``int32`` and ``float`` variants of this function:
::
void aos_to_soa3(uniform float a[], float * uniform v0,
float * uniform v1, float * uniform v2)
void aos_to_soa3(uniform int32 a[], int32 * uniform v0,
int32 * uniform v1, int32 * uniform v2)
void aos_to_soa3(uniform float a[], varying float * uniform v0,
varying float * uniform v1, varying float * uniform v2)
void aos_to_soa3(uniform int32 a[], varying int32 * uniform v0,
varying int32 * uniform v1, varying int32 * uniform v2)
After computation is done, corresponding functions convert back from the
SoA values in ``ispc`` ``varying`` variables and write the values back to
@@ -3341,10 +3578,12 @@ the given array, starting at the given offset.
::
void aos_to_soa4(uniform float a[], float * uniform v0, float * uniform v1,
float * uniform v2, float * uniform v3)
void aos_to_soa4(uniform int32 a[], int32 * uniform v0, int32 * uniform v1,
int32 * uniform v2, int32 * uniform v3)
void aos_to_soa4(uniform float a[], varying float * uniform v0,
varying float * uniform v1, varying float * uniform v2,
varying float * uniform v3)
void aos_to_soa4(uniform int32 a[], varying int32 * uniform v0,
varying int32 * uniform v1, varying int32 * uniform v2,
varying int32 * uniform v3)
void soa_to_aos4(float v0, float v1, float v2, float v3, uniform float a[])
void soa_to_aos4(int32 v0, int32 v1, int32 v2, int32 v3, uniform int32 a[])
@@ -3793,12 +4032,12 @@ equivalents of them.) For example, given a structure in ``ispc``:
// ispc code
struct Node {
uniform int count;
uniform float pos[3];
int count;
float pos[3];
};
If the ``Node`` structure is used in the parameters to an ``export`` ed
function, then the header file generated by the ``ispc`` compiler will
If a ``uniform Node`` structure is used in the parameters to an ``export``
ed function, then the header file generated by the ``ispc`` compiler will
have a declaration like:
::
@@ -3814,9 +4053,9 @@ program instances, ``ispc`` prohibits any varying types from being used in
parameters to functions with the ``export`` qualifier. (``ispc`` also
prohibits passing structures that themselves have varying types as members,
etc.) Thus, all datatypes that is shared with the application must have
the ``uniform`` qualifier applied to them. (See `Understanding How to
Interoperate With the Application's Data`_ for more discussion of how to
load vectors of SoA or AoSoA data from the application.)
the ``uniform`` or ``soa`` rate qualifier applied to them. (See `Use
"Structure of Arrays" Layout When Possible`_ in the Performance Guide for
more discussion of how to load vectors of SOA data from the application.)
Similarly, ``struct`` types shared with the application can also have
embedded pointers.
@@ -3834,7 +4073,7 @@ On the ``ispc`` side, the corresponding ``struct`` declaration is:
// ispc
struct Foo {
uniform float * uniform foo, * uniform bar;
float * uniform foo, * uniform bar;
};
There is one subtlety related to data layout to be aware of: ``ispc``
@@ -3911,156 +4150,6 @@ program instances improves performance.
.. _ispc Performance Tuning Guide: http://ispc.github.com/perf.html
Understanding How to Interoperate With the Application's Data
-------------------------------------------------------------
One of ``ispc``'s key goals is to be able to interoperate with the
application's data, in whatever layout it is stored in. You don't need to
worry about reformatting of data or the overhead of a driver model that
abstracts the data layout. This section illustrates some of the
alternatives with a simple example of computing the length of a large
number of vectors.
Consider for starters a ``Vector`` data-type, defined in C as:
::
struct Vector { float x, y, z; };
We might have (still in C) an array of ``Vector`` s defined like this:
::
Vector vectors[1024];
This is called an "array of structures" (AoS) layout. To compute the
lengths of these vectors in parallel, you can write ``ispc`` code like
this:
::
export void length(Vector vectors[1024], uniform float len[]) {
foreach (index = 0 ... 1024) {
float x = vectors[index].x;
float y = vectors[index].y;
float z = vectors[index].z;
float l = sqrt(x*x + y*y + z*z);
len[index] = l;
}
}
The problem with this implementation is that the indexing into the array of
structures, ``vectors[index].x`` is relatively expensive. On a target
machine that supports four-wide Intel® SSE, this turns into four loads of
single ``float`` values from non-contiguous memory locations, which are
then packed into a four-wide register corresponding to ``float x``. Once the
values are loaded into the local ``x``, ``y``, and ``z`` variables,
SIMD-efficient computation can proceed; getting to that point is
relatively inefficient.
(As described previously in `Converting Between Array-of-Structures and
Structure-of-Arrays Layout`_, this computation could be written more
efficiently using standard library routines to convert from the AoS layout,
if we were given a flat array of ``float`` values.)
An alternative data layout would be the "structure of arrays" (SoA). In C,
the data would be declared as:
::
float x[1024], y[1024], z[1024];
The ``ispc`` code might be:
::
export void length(uniform float x[1024], uniform float y[1024],
uniform float z[1024], uniform float len[]) {
foreach (index = 0 ... 1024) {
float xx = x[index];
float yy = y[index];
float zz = z[index];
float l = sqrt(xx*xx + yy*yy + zz*zz);
len[index] = l;
}
}
In this example, the loads into ``xx``, ``yy``, and ``zz`` are single
vector loads of an entire gang's worth of values into the corresponding
registers. This processing is more efficient than the multiple scalar
loads that are required with the AoS layout above.
A final alternative is "array of structures of arrays" (AoSoA), a hybrid
between these two. A structure is declared that stores a small number of
``x``, ``y``, and ``z`` values in contiguous memory locations:
::
struct Vector16 {
float x[16], y[16], z[16];
};
The ``ispc`` code has an outer loop over ``Vector16`` elements and
then an inner loop that peels off values from the element members:
::
#define N_VEC (1024/16)
export void length(Vector16 v[N_VEC], uniform float len[]) {
foreach (i = 0 ... N_VEC, j = 0 ... 16) {
float x = v[i].x[j];
float y = v[i].y[j];
float z = v[i].z[j];
float l = sqrt(x*x + y*y + z*z);
len[16*i+j] = l;
}
}
}
One advantage of the AoSoA layout is that the memory accesses to load
values are to nearby memory locations, where as with SoA, each of the three
loads above is to locations separated by a few thousand bytes. Thus, AoSoA
can be more cache friendly. For structures with many members, this
difference can lead to a substantial improvement.
With some additional complexity, ``ispc`` can also generate code that
efficiently processes data in AoSoA layout where the inner array length is
less than the machine vector width. For example, consider doing
computation with this AoSoA structure definition on a machine with an
8-wide vector unit (for example, an Intel® AVX target):
::
struct Vector4 {
float x[4], y[4], z[4];
};
The ``ispc`` code to process this loads elements four at a time from
``Vector4`` instances until it has a full ``programCount`` number of
elements to work with and then proceeds with the computation.
::
#define N_VEC (1024/4)
export void length(Vector4 v[N_VEC], uniform float len[]) {
for (uniform int i = 0; i < N_VEC; i += programCount / 4) {
float x, y, z;
for (uniform int j = 0; j < programCount / 4; ++j) {
if (programIndex >= 4 * j &&
programIndex < 4 * (j+1)) {
int index = (programIndex & 0x3);
x = v[i+j].x[index];
y = v[i+j].y[index];
z = v[i+j].z[index];
}
}
float l = sqrt(x*x + y*y + z*z);
len[4*i + programIndex] = l;
}
}
Disclaimer and Legal Information
================================

28
docs/news.rst Normal file
View File

@@ -0,0 +1,28 @@
=========
ispc News
=========
ispc 1.1.4 is Released
----------------------
On February 4, 2012, the 1.1.4 release of ``ispc`` was posted; new features
include ``new`` and ``delete`` for dynamic memory allocation in ``ispc``
programs, "local" atomic operations in the standard library, and a new
scalar compilation target. See the `1.1.4 release notes`_ for details.
.. _1.1.4 release notes: https://github.com/ispc/ispc/tree/master/docs/ReleaseNotes.txt
ispc 1.1.3 is Released
----------------------
With this release, the language now supports "switch" statements, with the same semantics and syntax as in C.
This release includes fixes for two important performance related issues:
the quality of code generated for "foreach" statements has been
substantially improved, and performance regression with code for "gathers"
that was introduced in v1.1.2 has been fixed in this release.
Thanks to Jean-Luc Duprat for a number of patches that improve support for
building on various platforms, and to Pierre-Antoine Lacaze for patches so
that ispc builds under MinGW.

View File

@@ -13,6 +13,7 @@ the most out of ``ispc`` in practice.
+ `Improving Control Flow Coherence With "foreach_tiled"`_
+ `Using Coherent Control Flow Constructs`_
+ `Use "uniform" Whenever Appropriate`_
+ `Use "Structure of Arrays" Layout When Possible`_
* `Tips and Techniques`_
@@ -247,6 +248,76 @@ but it's always best to provide the compiler with as much help as possible
to understand the actual form of your computation.
Use "Structure of Arrays" Layout When Possible
----------------------------------------------
In general, memory access performance (for both reads and writes) is best
when the running program instances access a contiguous region of memory; in
this case efficient vector load and store instructions can often be used
rather than gathers and scatters. As an example of this issue, consider an
array of a simple point datatype laid out and accessed in conventional
"array of structures" (AOS) layout:
::
struct Point { float x, y, z; };
uniform Point pts[...];
float v = pts[programIndex].x;
In the above code, the access to ``pts[programIndex].x`` accesses
non-sequential memory locations, due to the ``y`` and ``z`` values between
the desired ``x`` values in memory. A "gather" is required to get the
value of ``v``, with a corresponding decrease in performance.
If ``Point`` was defined as a "structure of arrays" (SOA) type, the access
can be much more efficient:
::
struct Point8 { float x[8], y[8], z[8]; };
uniform Point8 pts8[...];
int majorIndex = programIndex / 8;
int minorIndex = programIndex % 8;
float v = pts8[majorIndex].x[minorIndex];
In this case, each ``Point8`` has 8 ``x`` values contiguous in memory
before 8 ``y`` values and then 8 ``z`` values. If the gang size is 8 or
less, the access for ``v`` will have the same value of ``majorIndex`` for
all program instances and will access consecutive elements of the ``x[8]``
array with a vector load. (For larger gang sizes, two 8-wide vector loads
would be issues, which is also quite efficient.)
However, the syntax in the above code is messy; accessing SOA data in this
fashion is much less elegant than the corresponding code for accessing the
data with AOS layout. The ``soa`` qualifier in ``ispc`` can be used to
cause the corresponding transformation to be made to the ``Point`` type,
while preserving the clean syntax for data access that comes with AOS
layout:
::
soa<8> Point pts[...];
float v = pts[programIndex].x;
Thanks to having SOA layout a first-class concept in the language's type
system, it's easy to write functions that convert data between the
layouts. For example, the ``aos_to_soa`` function below converts ``count``
elements of the given ``Point`` type from AOS to 8-wide SOA layout. (It
assumes that the caller has pre-allocated sufficient space in the
``pts_soa`` output array.
::
void aos_to_soa(uniform Point pts_aos[], uniform int count,
soa<8> pts_soa[]) {
foreach (i = 0 ... count)
pts_soa[i] = pts_aos[i];
}
Analogously, a function could be written to convert back from SOA to AOS if
needed.
Tips and Techniques
===================
@@ -339,6 +410,12 @@ based on the index, it can be worth doing. See the example
``examples/volume_rendering`` in the ``ispc`` distribution for the use of
this technique in an instance where it is beneficial to performance.
Understanding Memory Read Coalescing
------------------------------------
XXXX todo
Avoid 64-bit Addressing Calculations When Possible
--------------------------------------------------

65
docs/template-news.txt Normal file
View File

@@ -0,0 +1,65 @@
%(head_prefix)s
%(head)s
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1486404-4']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
%(stylesheet)s
%(body_prefix)s
<div id="wrap">
<div id="wrap2">
<div id="header">
<h1 id="logo">Intel SPMD Program Compiler</h1>
<div id="slogan">An open-source compiler for high-performance SIMD programming on
the CPU</div>
</div>
<div id="nav">
<div id="nbar">
<ul>
<li><a href="index.html">Overview</a></li>
<li id="selected"><a href="news.html">News</a></li>
<li><a href="features.html">Features</a></li>
<li><a href="downloads.html">Downloads</a></li>
<li><a href="documentation.html">Documentation</a></li>
<li><a href="perf.html">Performance</a></li>
</ul>
</div>
</div>
<div id="content-wrap">
<div id="sidebar">
<div class="widgetspace">
<h1>Resources</h1>
<ul class="menu">
<li><a href="http://github.com/ispc/ispc/">ispc page on github</a></li>
<li><a href="http://groups.google.com/group/ispc-users/">ispc
users mailing list</a></li>
<li><a href="http://groups.google.com/group/ispc-dev/">ispc
developers mailing list</a></li>
<li><a href="http://github.com/ispc/ispc/wiki/">Wiki</a></li>
<li><a href="http://github.com/ispc/ispc/issues/">Bug tracking</a></li>
<li><a href="doxygen/index.html">Doxygen</a></li>
</ul>
</div>
</div>
%(body_pre_docinfo)s
%(docinfo)s
<div id="content">
%(body)s
</div>
<div class="clearfix"></div>
<div id="footer"> &copy; 2011-2012 <strong>Intel Corporation</strong> | Valid <a href="http://validator.w3.org/check?uri=referer">XHTML</a> | <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> | ClearBlue by: <a href="http://www.themebin.com/">ThemeBin</a>
<!-- Please Do Not remove this link, thank u -->
</div>
</div>
</div>
</div>
%(body_suffix)s

View File

@@ -26,6 +26,7 @@
<div id="nbar">
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="news.html">News</a></li>
<li><a href="features.html">Features</a></li>
<li><a href="downloads.html">Downloads</a></li>
<li><a href="documentation.html">Documentation</a></li>
@@ -55,7 +56,7 @@
%(body)s
</div>
<div class="clearfix"></div>
<div id="footer"> &copy; 2011 <strong>Intel Corporation</strong> | Valid <a href="http://validator.w3.org/check?uri=referer">XHTML</a> | <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> | ClearBlue by: <a href="http://www.themebin.com/">ThemeBin</a>
<div id="footer"> &copy; 2011-2012 <strong>Intel Corporation</strong> | Valid <a href="http://validator.w3.org/check?uri=referer">XHTML</a> | <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> | ClearBlue by: <a href="http://www.themebin.com/">ThemeBin</a>
<!-- Please Do Not remove this link, thank u -->
</div>
</div>

View File

@@ -26,6 +26,7 @@
<div id="nbar">
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="news.html">News</a></li>
<li><a href="features.html">Features</a></li>
<li><a href="downloads.html">Downloads</a></li>
<li id="selected"><a href="documentation.html">Documentation</a></li>
@@ -55,7 +56,7 @@
%(body)s
</div>
<div class="clearfix"></div>
<div id="footer"> &copy; 2011 <strong>Intel Corporation</strong> | Valid <a href="http://validator.w3.org/check?uri=referer">XHTML</a> | <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> | ClearBlue by: <a href="http://www.themebin.com/">ThemeBin</a>
<div id="footer"> &copy; 2011-2012 <strong>Intel Corporation</strong> | Valid <a href="http://validator.w3.org/check?uri=referer">XHTML</a> | <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> | ClearBlue by: <a href="http://www.themebin.com/">ThemeBin</a>
<!-- Please Do Not remove this link, thank u -->
</div>
</div>

View File

@@ -39,9 +39,6 @@ example implementation of this function that counts the number of times the
callback is made and records some statistics about control flow coherence
is provided in the instrument.cpp file.
*** Note: on Linux, this example currently hits an assertion in LLVM during
*** compilation
Deferred
========
@@ -110,6 +107,13 @@ This program implements both the Black-Scholes and Binomial options pricing
models in both ispc and regular serial C++ code.
Perfbench
=========
This runs a number of microbenchmarks to measure system performance and
code generation quality.
RT
==

View File

@@ -50,7 +50,6 @@ struct Isect {
struct Sphere {
vec center;
float radius;
};
struct Plane {
@@ -83,7 +82,7 @@ static inline void vnormalize(vec &v) {
static void
ray_plane_intersect(Isect &isect, Ray &ray, Plane &plane) {
ray_plane_intersect(Isect &isect, Ray &ray, uniform Plane &plane) {
float d = -dot(plane.p, plane.n);
float v = dot(ray.dir, plane.n);
@@ -103,7 +102,7 @@ ray_plane_intersect(Isect &isect, Ray &ray, Plane &plane) {
static inline void
ray_sphere_intersect(Isect &isect, Ray &ray, Sphere &sphere) {
ray_sphere_intersect(Isect &isect, Ray &ray, uniform Sphere &sphere) {
vec rs = ray.org - sphere.center;
float B = dot(rs, ray.dir);
@@ -148,7 +147,7 @@ orthoBasis(vec basis[3], vec n) {
static float
ambient_occlusion(Isect &isect, Plane &plane, Sphere spheres[3],
ambient_occlusion(Isect &isect, uniform Plane &plane, uniform Sphere spheres[3],
RNGState &rngstate) {
float eps = 0.0001f;
vec p, n;
@@ -204,8 +203,8 @@ ambient_occlusion(Isect &isect, Plane &plane, Sphere spheres[3],
static void ao_scanlines(uniform int y0, uniform int y1, uniform int w,
uniform int h, uniform int nsubsamples,
uniform float image[]) {
static Plane plane = { { 0.0f, -0.5f, 0.0f }, { 0.f, 1.f, 0.f } };
static Sphere spheres[3] = {
static uniform Plane plane = { { 0.0f, -0.5f, 0.0f }, { 0.f, 1.f, 0.f } };
static uniform Sphere spheres[3] = {
{ { -2.0f, 0.0f, -3.5f }, 0.5f },
{ { -0.5f, 0.0f, -3.0f }, 0.5f },
{ { 1.0f, 0.0f, -2.2f }, 0.5f } };

View File

@@ -204,6 +204,7 @@ void WriteFrame(const char *filename, const InputData *input,
fprintf(out, "P6 %d %d 255\n", input->header.framebufferWidth,
input->header.framebufferHeight);
fwrite(framebufferAOS, imageBytes, 1, out);
fclose(out);
lAlignedFree(framebufferAOS);
}

View File

@@ -35,35 +35,35 @@
struct InputDataArrays
{
uniform float * uniform zBuffer;
uniform unsigned int16 * uniform normalEncoded_x; // half float
uniform unsigned int16 * uniform normalEncoded_y; // half float
uniform unsigned int16 * uniform specularAmount; // half float
uniform unsigned int16 * uniform specularPower; // half float
uniform unsigned int8 * uniform albedo_x; // unorm8
uniform unsigned int8 * uniform albedo_y; // unorm8
uniform unsigned int8 * uniform albedo_z; // unorm8
uniform float * uniform lightPositionView_x;
uniform float * uniform lightPositionView_y;
uniform float * uniform lightPositionView_z;
uniform float * uniform lightAttenuationBegin;
uniform float * uniform lightColor_x;
uniform float * uniform lightColor_y;
uniform float * uniform lightColor_z;
uniform float * uniform lightAttenuationEnd;
float *zBuffer;
unsigned int16 *normalEncoded_x; // half float
unsigned int16 *normalEncoded_y; // half float
unsigned int16 *specularAmount; // half float
unsigned int16 *specularPower; // half float
unsigned int8 *albedo_x; // unorm8
unsigned int8 *albedo_y; // unorm8
unsigned int8 *albedo_z; // unorm8
float *lightPositionView_x;
float *lightPositionView_y;
float *lightPositionView_z;
float *lightAttenuationBegin;
float *lightColor_x;
float *lightColor_y;
float *lightColor_z;
float *lightAttenuationEnd;
};
struct InputHeader
{
uniform float cameraProj[4][4];
uniform float cameraNear;
uniform float cameraFar;
float cameraProj[4][4];
float cameraNear;
float cameraFar;
uniform int32 framebufferWidth;
uniform int32 framebufferHeight;
uniform int32 numLights;
uniform int32 inputDataChunkSize;
uniform int32 inputDataArrayOffsets[idaNum];
int32 framebufferWidth;
int32 framebufferHeight;
int32 numLights;
int32 inputDataChunkSize;
int32 inputDataArrayOffsets[idaNum];
};
@@ -575,8 +575,6 @@ SplitTileMinMax(
uniform float light_positionView_z_array[],
uniform float light_attenuationEnd_array[],
// Outputs
// TODO: ISPC doesn't currently like multidimensionsal arrays so we'll do the
// indexing math ourselves
uniform int32 subtileIndices[],
uniform int32 subtileIndicesPitch,
uniform int32 subtileNumLights[]

View File

@@ -23,6 +23,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stencil", "stencil\stencil.
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "deferred_shading", "deferred\deferred_shading.vcxproj", "{87F53C53-957E-4E91-878A-BC27828FB9EB}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "perfbench", "perfbench\perfbench.vcxproj", "{D923BB7E-A7C8-4850-8FCF-0EB9CE35B4E8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -119,6 +121,14 @@ Global
{87F53C53-957E-4E91-878A-BC27828FB9EB}.Release|Win32.Build.0 = Release|Win32
{87F53C53-957E-4E91-878A-BC27828FB9EB}.Release|x64.ActiveCfg = Release|x64
{87F53C53-957E-4E91-878A-BC27828FB9EB}.Release|x64.Build.0 = Release|x64
{D923BB7E-A7C8-4850-8FCF-0EB9CE35B4E8}.Debug|Win32.ActiveCfg = Debug|Win32
{D923BB7E-A7C8-4850-8FCF-0EB9CE35B4E8}.Debug|Win32.Build.0 = Debug|Win32
{D923BB7E-A7C8-4850-8FCF-0EB9CE35B4E8}.Debug|x64.ActiveCfg = Debug|x64
{D923BB7E-A7C8-4850-8FCF-0EB9CE35B4E8}.Debug|x64.Build.0 = Debug|x64
{D923BB7E-A7C8-4850-8FCF-0EB9CE35B4E8}.Release|Win32.ActiveCfg = Release|Win32
{D923BB7E-A7C8-4850-8FCF-0EB9CE35B4E8}.Release|Win32.Build.0 = Release|Win32
{D923BB7E-A7C8-4850-8FCF-0EB9CE35B4E8}.Release|x64.ActiveCfg = Release|x64
{D923BB7E-A7C8-4850-8FCF-0EB9CE35B4E8}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -0,0 +1,7 @@
EXAMPLE=perbench
CPP_SRC=perfbench.cpp perfbench_serial.cpp
ISPC_SRC=perfbench.ispc
ISPC_TARGETS=sse2,sse4,avx
include ../common.mk

View File

@@ -0,0 +1,108 @@
/*
Copyright (c) 2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#define NOMINMAX
#pragma warning (disable: 4244)
#pragma warning (disable: 4305)
#endif
#include <stdio.h>
#include <algorithm>
#include "../timing.h"
#include "perfbench_ispc.h"
typedef void (FuncType)(float *, int, float *, float *);
struct PerfTest {
FuncType *aFunc;
const char *aName;
FuncType *bFunc;
const char *bName;
const char *testName;
};
extern void xyzSumAOS(float *a, int count, float *zeros, float *result);
extern void xyzSumSOA(float *a, int count, float *zeros, float *result);
static void
lInitData(float *ptr, int count) {
for (int i = 0; i < count; ++i)
ptr[i] = float(i) / (1024.f * 1024.f);
}
static PerfTest tests[] = {
{ xyzSumAOS, "serial", ispc::xyzSumAOS, "ispc", "AOS vector element sum (with coalescing)" },
{ xyzSumAOS, "serial", ispc::xyzSumAOSStdlib, "ispc", "AOS vector element sum (stdlib swizzle)" },
{ xyzSumAOS, "serial", ispc::xyzSumAOSNoCoalesce, "ispc", "AOS vector element sum (no coalescing)" },
{ xyzSumSOA, "serial", ispc::xyzSumSOA, "ispc", "SOA vector element sum" },
{ ispc::gathers, "gather", ispc::loads, "vector load", "Memory reads" },
{ ispc::scatters, "scatter", ispc::stores, "vector store", "Memory writes" },
};
int main() {
int count = 3*64*1024;
float *a = new float[count];
float zeros[32] = { 0 };
int nTests = sizeof(tests) / sizeof(tests[0]);
for (int i = 0; i < nTests; ++i) {
lInitData(a, count);
reset_and_start_timer();
float resultA[3] = { 0, 0, 0 };
for (int j = 0; j < 100; ++j)
tests[i].aFunc(a, count, zeros, resultA);
double aTime = get_elapsed_mcycles();
lInitData(a, count);
reset_and_start_timer();
float resultB[3] = { 0, 0, 0 };
for (int j = 0; j < 100; ++j)
tests[i].bFunc(a, count, zeros, resultB);
double bTime = get_elapsed_mcycles();
printf("%-40s: [%.2f] M cycles %s, [%.2f] M cycles %s (%.2fx speedup).\n",
tests[i].testName, aTime, tests[i].aName, bTime, tests[i].bName,
aTime/bTime);
#if 0
printf("\t(%f %f %f) - (%f %f %f)\n", resultSerial[0], resultSerial[1],
resultSerial[2], resultISPC[0], resultISPC[1], resultISPC[2]);
#endif
}
return 0;
}

View File

@@ -0,0 +1,170 @@
/*
Copyright (c) 2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
export void xyzSumAOS(uniform float array[], uniform int count,
uniform float zeros[], uniform float result[]) {
float xsum = 0, ysum = 0, zsum = 0;
foreach (i = 0 ... count/3) {
float x = array[3*i];
float y = array[3*i+1];
float z = array[3*i+2];
xsum += x;
ysum += y;
zsum += z;
}
result[0] = reduce_add(xsum);
result[1] = reduce_add(ysum);
result[2] = reduce_add(zsum);
}
export void xyzSumAOSStdlib(uniform float array[], uniform int count,
uniform float zeros[], uniform float result[]) {
float xsum = 0, ysum = 0, zsum = 0;
for (uniform int i = 0; i < 64*1024 /*count/3*/; i += programCount) {
float x, y, z;
aos_to_soa3(&array[3*i], &x, &y, &z);
xsum += x;
ysum += y;
zsum += z;
}
result[0] = reduce_add(xsum);
result[1] = reduce_add(ysum);
result[2] = reduce_add(zsum);
}
export void xyzSumAOSNoCoalesce(uniform float array[], uniform int count,
uniform float zerosArray[], uniform float result[]) {
int zeros = zerosArray[programIndex];
float xsum = 0, ysum = 0, zsum = 0;
foreach (i = 0 ... count/3) {
float x = array[3*i+zeros];
float y = array[3*i+1+zeros];
float z = array[3*i+2+zeros];
xsum += x;
ysum += y;
zsum += z;
}
result[0] = reduce_add(xsum);
result[1] = reduce_add(ysum);
result[2] = reduce_add(zsum);
}
export void xyzSumSOA(uniform float array[], uniform int count,
uniform float zeros[], uniform float result[]) {
float xsum = 0, ysum = 0, zsum = 0;
uniform float * uniform ap = array;
assert(programCount <= 8);
for (uniform int i = 0; i < count/3; i += 8, ap += 24) {
for (uniform int j = 0; j < 8; j += programCount) {
float x = ap[j + programIndex];
float y = ap[8 + j + programIndex];
float z = ap[16 + j + programIndex];
xsum += x;
ysum += y;
zsum += z;
}
}
result[0] = reduce_add(xsum);
result[1] = reduce_add(ysum);
result[2] = reduce_add(zsum);
}
export void gathers(uniform float array[], uniform int count,
uniform float zeros[], uniform float result[]) {
float sum = 0;
int zero = zeros[programIndex];
foreach (i = 0 ... count)
sum += array[i + zero];
result[0] = reduce_add(sum);
}
export void loads(uniform float array[], uniform int count,
uniform float zeros[], uniform float result[]) {
float sum = 0;
foreach (i = 0 ... count)
sum += array[i];
result[0] = reduce_add(sum);
}
export void scatters(uniform float array[], uniform int count,
uniform float zeros[], uniform float result[]) {
int zero = zeros[programIndex];
foreach (i = 0 ... count)
array[i + zero] = zero;
}
export void stores(uniform float array[], uniform int count,
uniform float zeros[], uniform float result[]) {
int zero = zeros[programIndex];
foreach (i = 0 ... count)
array[i] = zero;
}
export void normalizeAOSNoCoalesce(uniform float array[], uniform int count,
uniform float zeroArray[]) {
float zeros = zeroArray[programIndex];
foreach (i = 0 ... count/3) {
float x = array[3*i+zeros];
float y = array[3*i+1+zeros];
float z = array[3*i+2+zeros];
float l2 = x*x + y*y + z*z;
array[3*i] /= l2;
array[3*i+1] /= l2;
array[3*i+2] /= l2;
}
}
export void normalizeSOA(uniform float array[], uniform int count,
uniform float zeros[]) {
foreach (i = 0 ... count/3) {
float x = array[3*i];
float y = array[3*i+1];
float z = array[3*i+2];
float l2 = x*x + y*y + z*z;
array[3*i] /= l2;
array[3*i+1] /= l2;
array[3*i+2] /= l2;
}
}

View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{d923bb7e-a7c8-4850-8fcf-0eb9ce35b4e8}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>perfbench</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<ExecutablePath>$(ProjectDir)..\..;$(ExecutablePath)</ExecutablePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<ExecutablePath>$(ProjectDir)..\..;$(ExecutablePath)</ExecutablePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<ExecutablePath>$(ProjectDir)..\..;$(ExecutablePath)</ExecutablePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<ExecutablePath>$(ProjectDir)..\..;$(ExecutablePath)</ExecutablePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(TargetDir)</AdditionalIncludeDirectories>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(TargetDir)</AdditionalIncludeDirectories>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(TargetDir)</AdditionalIncludeDirectories>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(TargetDir)</AdditionalIncludeDirectories>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="perfbench.cpp" />
<ClCompile Include="perfbench_serial.cpp" />
</ItemGroup>
<ItemGroup>
<CustomBuild Include="perfbench.ispc">
<FileType>Document</FileType>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">ispc -O2 %(Filename).ispc -o $(TargetDir)%(Filename).obj -h $(TargetDir)%(Filename)_ispc.h --arch=x86 --target=sse2,sse4,avx
</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">ispc -O2 %(Filename).ispc -o $(TargetDir)%(Filename).obj -h $(TargetDir)%(Filename)_ispc.h --target=sse2,sse4,avx
</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(TargetDir)%(Filename).obj;$(TargetDir)%(Filename)_sse2.obj;$(TargetDir)%(Filename)_sse4.obj;$(TargetDir)%(Filename)_avx.obj;$(TargetDir)%(Filename)_ispc.h</Outputs>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(TargetDir)%(Filename).obj;$(TargetDir)%(Filename)_sse2.obj;$(TargetDir)%(Filename)_sse4.obj;$(TargetDir)%(Filename)_avx.obj;$(TargetDir)%(Filename)_ispc.h</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ispc -O2 %(Filename).ispc -o $(TargetDir)%(Filename).obj -h $(TargetDir)%(Filename)_ispc.h --arch=x86 --target=sse2,sse4,avx
</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ispc -O2 %(Filename).ispc -o $(TargetDir)%(Filename).obj -h $(TargetDir)%(Filename)_ispc.h --target=sse2,sse4,avx
</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(TargetDir)%(Filename).obj;$(TargetDir)%(Filename)_sse2.obj;$(TargetDir)%(Filename)_sse4.obj;$(TargetDir)%(Filename)_avx.obj;$(TargetDir)%(Filename)_ispc.h</Outputs>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(TargetDir)%(Filename).obj;$(TargetDir)%(Filename)_sse2.obj;$(TargetDir)%(Filename)_sse4.obj;$(TargetDir)%(Filename)_avx.obj;$(TargetDir)%(Filename)_ispc.h</Outputs>
</CustomBuild>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,61 @@
/*
Copyright (c) 2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <math.h>
void
xyzSumAOS(float *a, int count, float *zeros, float *result) {
float xsum = 0, ysum = 0, zsum = 0;
for (int i = 0; i < count; i += 3) {
xsum += a[i];
ysum += a[i+1];
zsum += a[i+2];
}
result[0] = xsum;
result[1] = ysum;
result[2] = zsum;
}
void
xyzSumSOA(float *a, int count, float *zeros, float *result) {
float xsum = 0, ysum = 0, zsum = 0;
for (int i = 0; i < count/3; ++i) {
float *p = a + (i >> 3) * 24 + (i & 7);
xsum += p[0];
ysum += p[8];
zsum += p[16];
}
result[0] = xsum;
result[1] = ysum;
result[2] = zsum;
}

View File

@@ -43,17 +43,17 @@ struct Ray {
};
struct Triangle {
uniform float p[3][4];
uniform int id;
uniform int pad[3];
float p[3][4];
int id;
int pad[3];
};
struct LinearBVHNode {
uniform float bounds[2][3];
uniform unsigned int offset; // num primitives for leaf, second child for interior
uniform unsigned int8 nPrimitives;
uniform unsigned int8 splitAxis;
uniform unsigned int16 pad;
float bounds[2][3];
unsigned int offset; // num primitives for leaf, second child for interior
unsigned int8 nPrimitives;
unsigned int8 splitAxis;
unsigned int16 pad;
};
static inline float3 Cross(const float3 v1, const float3 v2) {
@@ -88,9 +88,12 @@ static void generateRay(uniform const float raster2camera[4][4],
camy /= camw;
camz /= camw;
ray.dir.x = camera2world[0][0] * camx + camera2world[0][1] * camy + camera2world[0][2] * camz;
ray.dir.y = camera2world[1][0] * camx + camera2world[1][1] * camy + camera2world[1][2] * camz;
ray.dir.z = camera2world[2][0] * camx + camera2world[2][1] * camy + camera2world[2][2] * camz;
ray.dir.x = camera2world[0][0] * camx + camera2world[0][1] * camy +
camera2world[0][2] * camz;
ray.dir.y = camera2world[1][0] * camx + camera2world[1][1] * camy +
camera2world[1][2] * camz;
ray.dir.z = camera2world[2][0] * camx + camera2world[2][1] * camy +
camera2world[2][2] * camz;
ray.origin.x = camera2world[0][3] / camera2world[3][3];
ray.origin.y = camera2world[1][3] / camera2world[3][3];
@@ -143,7 +146,7 @@ static bool BBoxIntersect(const uniform float bounds[2][3],
static bool TriIntersect(const Triangle &tri, Ray &ray) {
static bool TriIntersect(const uniform Triangle &tri, Ray &ray) {
uniform float3 p0 = { tri.p[0][0], tri.p[0][1], tri.p[0][2] };
uniform float3 p1 = { tri.p[1][0], tri.p[1][1], tri.p[1][2] };
uniform float3 p2 = { tri.p[2][0], tri.p[2][1], tri.p[2][2] };
@@ -183,8 +186,8 @@ static bool TriIntersect(const Triangle &tri, Ray &ray) {
}
bool BVHIntersect(const LinearBVHNode nodes[], const Triangle tris[],
Ray &r) {
bool BVHIntersect(const uniform LinearBVHNode nodes[],
const uniform Triangle tris[], Ray &r) {
Ray ray = r;
bool hit = false;
// Follow ray through BVH nodes to find primitive intersections
@@ -193,7 +196,7 @@ bool BVHIntersect(const LinearBVHNode nodes[], const Triangle tris[],
while (true) {
// Check ray against BVH node
LinearBVHNode node = nodes[nodeNum];
uniform LinearBVHNode node = nodes[nodeNum];
if (any(BBoxIntersect(node.bounds, ray))) {
uniform unsigned int nPrimitives = node.nPrimitives;
if (nPrimitives > 0) {
@@ -239,8 +242,8 @@ static void raytrace_tile(uniform int x0, uniform int x1,
const uniform float raster2camera[4][4],
const uniform float camera2world[4][4],
uniform float image[], uniform int id[],
const LinearBVHNode nodes[],
const Triangle triangles[]) {
const uniform LinearBVHNode nodes[],
const uniform Triangle triangles[]) {
uniform float widthScale = (float)(baseWidth) / (float)(width);
uniform float heightScale = (float)(baseHeight) / (float)(height);
@@ -262,8 +265,8 @@ export void raytrace_ispc(uniform int width, uniform int height,
const uniform float raster2camera[4][4],
const uniform float camera2world[4][4],
uniform float image[], uniform int id[],
const LinearBVHNode nodes[],
const Triangle triangles[]) {
const uniform LinearBVHNode nodes[],
const uniform Triangle triangles[]) {
raytrace_tile(0, width, 0, height, width, height, baseWidth, baseHeight,
raster2camera, camera2world, image,
id, nodes, triangles);
@@ -275,8 +278,8 @@ task void raytrace_tile_task(uniform int width, uniform int height,
const uniform float raster2camera[4][4],
const uniform float camera2world[4][4],
uniform float image[], uniform int id[],
const LinearBVHNode nodes[],
const Triangle triangles[]) {
const uniform LinearBVHNode nodes[],
const uniform Triangle triangles[]) {
uniform int dx = 16, dy = 16; // must match dx, dy below
uniform int xBuckets = (width + (dx-1)) / dx;
uniform int x0 = (taskIndex % xBuckets) * dx;
@@ -295,8 +298,8 @@ export void raytrace_ispc_tasks(uniform int width, uniform int height,
const uniform float raster2camera[4][4],
const uniform float camera2world[4][4],
uniform float image[], uniform int id[],
const LinearBVHNode nodes[],
const Triangle triangles[]) {
const uniform LinearBVHNode nodes[],
const uniform Triangle triangles[]) {
uniform int dx = 16, dy = 16;
uniform int xBuckets = (width + (dx-1)) / dx;
uniform int yBuckets = (height + (dy-1)) / dy;

View File

@@ -123,9 +123,12 @@ static void generateRay(const float raster2camera[4][4],
camy /= camw;
camz /= camw;
ray.dir.x = camera2world[0][0] * camx + camera2world[0][1] * camy + camera2world[0][2] * camz;
ray.dir.y = camera2world[1][0] * camx + camera2world[1][1] * camy + camera2world[1][2] * camz;
ray.dir.z = camera2world[2][0] * camx + camera2world[2][1] * camy + camera2world[2][2] * camz;
ray.dir.x = camera2world[0][0] * camx + camera2world[0][1] * camy +
camera2world[0][2] * camz;
ray.dir.y = camera2world[1][0] * camx + camera2world[1][1] * camy +
camera2world[1][2] * camz;
ray.dir.z = camera2world[2][0] * camx + camera2world[2][1] * camy +
camera2world[2][2] * camz;
ray.origin.x = camera2world[0][3] / camera2world[3][3];
ray.origin.y = camera2world[1][3] / camera2world[3][3];

1337
expr.cpp

File diff suppressed because it is too large Load Diff

1
expr.h
View File

@@ -299,7 +299,6 @@ public:
llvm::Value *GetValue(FunctionEmitContext *ctx) const;
llvm::Value *GetLValue(FunctionEmitContext *ctx) const;
const Type *GetType() const;
const Type *GetLValueType() const;
Symbol *GetBaseSymbol() const;
void Print() const;
Expr *Optimize();

View File

@@ -355,7 +355,7 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function,
// issue a warning. Also need to warn if it's the entry block for
// the function (in which case it will not have predeccesors but is
// still reachable.)
if (type->GetReturnType() != AtomicType::Void &&
if (Type::Equal(type->GetReturnType(), AtomicType::Void) == false &&
(pred_begin(ec.bblock) != pred_end(ec.bblock) || (ec.bblock == entryBBlock)))
Warning(sym->pos, "Missing return statement in function returning \"%s\".",
type->rType->GetString().c_str());

View File

@@ -497,6 +497,7 @@ Opt::Opt() {
disableMaskedStoreToStore = false;
disableGatherScatterFlattening = false;
disableUniformMemoryOptimizations = false;
disableCoalescing = false;
}
///////////////////////////////////////////////////////////////////////////

5
ispc.h
View File

@@ -107,6 +107,7 @@ class ExprList;
class Function;
class FunctionType;
class Module;
class PointerType;
class Stmt;
class Symbol;
class SymbolTable;
@@ -339,6 +340,10 @@ struct Opt {
than gathers/scatters. This is likely only useful for measuring
the impact of this optimization. */
bool disableUniformMemoryOptimizations;
/** Disables optimizations that coalesce incoherent scalar memory
access from gathers into wider vector operations, when possible. */
bool disableCoalescing;
};
/** @brief This structure collects together a number of global variables.

View File

@@ -469,6 +469,42 @@ LLVMBoolVector(const bool *bvec) {
}
llvm::Constant *
LLVMIntAsType(int64_t val, LLVM_TYPE_CONST llvm::Type *type) {
LLVM_TYPE_CONST llvm::VectorType *vecType =
llvm::dyn_cast<LLVM_TYPE_CONST llvm::VectorType>(type);
if (vecType != NULL) {
llvm::Constant *v = llvm::ConstantInt::get(vecType->getElementType(),
val, true /* signed */);
std::vector<llvm::Constant *> vals;
for (int i = 0; i < (int)vecType->getNumElements(); ++i)
vals.push_back(v);
return llvm::ConstantVector::get(vals);
}
else
return llvm::ConstantInt::get(type, val, true /* signed */);
}
llvm::Constant *
LLVMUIntAsType(uint64_t val, LLVM_TYPE_CONST llvm::Type *type) {
LLVM_TYPE_CONST llvm::VectorType *vecType =
llvm::dyn_cast<LLVM_TYPE_CONST llvm::VectorType>(type);
if (vecType != NULL) {
llvm::Constant *v = llvm::ConstantInt::get(vecType->getElementType(),
val, false /* unsigned */);
std::vector<llvm::Constant *> vals;
for (int i = 0; i < (int)vecType->getNumElements(); ++i)
vals.push_back(v);
return llvm::ConstantVector::get(vals);
}
else
return llvm::ConstantInt::get(type, val, false /* unsigned */);
}
/** Conservative test to see if two llvm::Values are equal. There are
(potentially many) cases where the two values actually are equal but
this will return false. However, if it does return true, the two
@@ -585,7 +621,7 @@ LLVMFlattenInsertChain(llvm::InsertElementInst *ie, int vectorWidth,
llvm::dyn_cast<llvm::ConstantVector>(insertBase);
Assert(cv != NULL);
Assert(iOffset < (int)cv->getNumOperands());
elements[iOffset] = cv->getOperand(iOffset);
elements[iOffset] = cv->getOperand((int32_t)iOffset);
}
}
}

View File

@@ -173,6 +173,14 @@ extern llvm::Constant *LLVMFloatVector(float f);
across all elements */
extern llvm::Constant *LLVMDoubleVector(double f);
/** Returns a constant integer or vector (according to the given type) of
the given signed integer value. */
extern llvm::Constant *LLVMIntAsType(int64_t, LLVM_TYPE_CONST llvm::Type *t);
/** Returns a constant integer or vector (according to the given type) of
the given unsigned integer value. */
extern llvm::Constant *LLVMUIntAsType(uint64_t, LLVM_TYPE_CONST llvm::Type *t);
/** Returns an LLVM boolean vector based on the given array of values.
The array should have g->target.vectorWidth elements. */
extern llvm::Constant *LLVMBoolVector(const bool *v);

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2011, Intel Corporation
Copyright (c) 2010-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -141,16 +141,17 @@ devUsage(int ret) {
printf(" [--fuzz-test]\t\t\tRandomly perturb program input to test error conditions\n");
printf(" [--fuzz-seed=<value>]\t\tSeed value for RNG for fuzz testing\n");
printf(" [--opt=<option>]\t\t\tSet optimization option\n");
printf(" disable-all-on-optimizations\n");
printf(" disable-all-on-optimizations\t\tDisable optimizations that take advantage of \"all on\" mask\n");
printf(" disable-blended-masked-stores\t\tScalarize masked stores on SSE (vs. using vblendps)\n");
printf(" disable-blending-removal\t\tDisable eliminating blend at same scope\n");
printf(" disable-coalescing\t\t\tDisable gather coalescing\n");
printf(" disable-coherent-control-flow\t\tDisable coherent control flow optimizations\n");
printf(" disable-gather-scatter-flattening\tDisable flattening when all lanes are on\n");
printf(" disable-gather-scatter-optimizations\tDisable improvements to gather/scatter\n");
printf(" disable-handle-pseudo-memory-ops\n");
printf(" disable-handle-pseudo-memory-ops\tLeave __pseudo_* calls for gather/scatter/etc. in final IR\n");
printf(" disable-uniform-control-flow\t\tDisable uniform control flow optimizations\n");
printf(" disable-uniform-memory-optimizations\tDisable uniform-based coherent memory access\n");
printf(" [--yydebug]\t\t\tPrint debugging information during parsing\n");
printf(" [--yydebug]\t\t\t\tPrint debugging information during parsing\n");
exit(ret);
}
@@ -223,8 +224,6 @@ int main(int Argc, char *Argv[]) {
LLVMInitializeX86TargetMC();
#endif
AtomicType::Init();
char *file = NULL;
const char *headerFileName = NULL;
const char *outFileName = NULL;
@@ -341,6 +340,8 @@ int main(int Argc, char *Argv[]) {
// optimizations
else if (!strcmp(opt, "disable-all-on-optimizations"))
g->opt.disableMaskAllOnOptimizations = true;
else if (!strcmp(opt, "disable-coalescing"))
g->opt.disableCoalescing = true;
else if (!strcmp(opt, "disable-handle-pseudo-memory-ops"))
g->opt.disableHandlePseudoMemoryOps = true;
else if (!strcmp(opt, "disable-blended-masked-stores"))

View File

@@ -153,6 +153,9 @@ Module::CompileFile() {
llvm::UnsafeFPMath = true;
#endif // !LLVM_3_1svn
extern void ParserInit();
ParserInit();
// FIXME: it'd be nice to do this in the Module constructor, but this
// function ends up calling into routines that expect the global
// variable 'm' to be initialized and available (which it isn't until
@@ -161,9 +164,6 @@ Module::CompileFile() {
bool runPreprocessor = g->runCPP;
extern void ParserInit();
ParserInit();
if (runPreprocessor) {
if (filename != NULL) {
// Try to open the file first, since otherwise we crash in the
@@ -241,7 +241,7 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
return;
}
if (sym->type == AtomicType::Void) {
if (Type::Equal(sym->type, AtomicType::Void)) {
Error(sym->pos, "\"void\" type global variable is illegal.");
return;
}
@@ -328,23 +328,39 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
}
/** Given an arbitrary type, see if it or any of the leaf types contained
in it has a type that's illegal to have exported to C/C++
code--specifically, that it has a varying value in memory, or a pointer
to SOA data (which has a different representation than a regular
pointer.
(Note that it's fine for the original struct or a contained struct to
be varying, so long as all of its members have bound 'uniform'
variability.)
/** Given an arbitrary type, see if it or any of the types contained in it
are varying. Returns true if so, false otherwise.
This functions returns true and issues an error if are any illegal
types are found and returns false otherwise.
*/
static bool
lRecursiveCheckVarying(const Type *t) {
lRecursiveCheckValidParamType(const Type *t) {
t = t->GetBaseType();
if (t->IsVaryingType()) return true;
const StructType *st = dynamic_cast<const StructType *>(t);
if (st) {
if (st != NULL) {
for (int i = 0; i < st->GetElementCount(); ++i)
if (lRecursiveCheckVarying(st->GetElementType(i)))
if (lRecursiveCheckValidParamType(st->GetElementType(i)))
return true;
return false;
}
else {
if (t->IsVaryingType())
return true;
const PointerType *pt = dynamic_cast<const PointerType *>(t);
if (pt != NULL && pt->IsSlice())
return true;
else
return false;
}
return false;
}
@@ -356,7 +372,7 @@ lRecursiveCheckVarying(const Type *t) {
static void
lCheckForVaryingParameter(const Type *type, const std::string &name,
SourcePos pos) {
if (lRecursiveCheckVarying(type)) {
if (lRecursiveCheckValidParamType(type)) {
const Type *t = type->GetBaseType();
if (dynamic_cast<const StructType *>(t))
Error(pos, "Struct parameter \"%s\" with varying member(s) is illegal "
@@ -369,10 +385,8 @@ lCheckForVaryingParameter(const Type *type, const std::string &name,
/** Given a function type, loop through the function parameters and see if
any are StructTypes. If so, issue an error (this seems to be broken
currently).
@todo Fix passing structs from C/C++ to ispc functions.
any are StructTypes. If so, issue an error; this is currently broken
(https://github.com/ispc/ispc/issues/3).
*/
static void
lCheckForStructParameters(const FunctionType *ftype, SourcePos pos) {
@@ -380,7 +394,7 @@ lCheckForStructParameters(const FunctionType *ftype, SourcePos pos) {
const Type *type = ftype->GetParameterType(i);
if (dynamic_cast<const StructType *>(type) != NULL) {
Error(pos, "Passing structs to/from application functions is "
"currently broken. Use a pointer or const pointer to the "
"currently broken. Use a pointer or const pointer to the "
"struct instead for now.");
return;
}
@@ -509,11 +523,12 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) {
// Make sure that the return type isn't 'varying' if the function is
// 'export'ed.
if (funSym->storageClass == SC_EXPORT &&
lRecursiveCheckVarying(functionType->GetReturnType()))
lRecursiveCheckValidParamType(functionType->GetReturnType()))
Error(funSym->pos, "Illegal to return a \"varying\" type from exported "
"function \"%s\"", funSym->name.c_str());
if (functionType->isTask && (functionType->GetReturnType() != AtomicType::Void))
if (functionType->isTask &&
Type::Equal(functionType->GetReturnType(), AtomicType::Void) == false)
Error(funSym->pos, "Task-qualified functions must have void return type.");
if (functionType->isExported || functionType->isExternC)
@@ -823,7 +838,13 @@ lEmitStructDecls(std::vector<const StructType *> &structTypes, FILE *file) {
// sorted ones in order.
for (unsigned int i = 0; i < sortedTypes.size(); ++i) {
const StructType *st = sortedTypes[i];
fprintf(file, "struct %s {\n", st->GetStructName().c_str());
fprintf(file, "struct %s", st->GetStructName().c_str());
if (st->GetSOAWidth() > 0)
// This has to match the naming scheme in
// StructType::GetCDeclaration().
fprintf(file, "_SOA%d", st->GetSOAWidth());
fprintf(file, " {\n");
for (int j = 0; j < st->GetElementCount(); ++j) {
const Type *type = st->GetElementType(j)->GetAsNonConstType();
std::string d = type->GetCDeclaration(st->GetElementName(j));
@@ -1001,9 +1022,10 @@ static void
lPrintExternGlobals(FILE *file, const std::vector<Symbol *> &externGlobals) {
for (unsigned int i = 0; i < externGlobals.size(); ++i) {
Symbol *sym = externGlobals[i];
if (lRecursiveCheckVarying(sym->type))
Warning(sym->pos, "Not emitting declaration for symbol \"%s\" into generated "
"header file since it (or some of its members) are varying.",
if (lRecursiveCheckValidParamType(sym->type))
Warning(sym->pos, "Not emitting declaration for symbol \"%s\" into "
"generated header file since it (or some of its members) "
"has types that are illegal in exported symbols.",
sym->name.c_str());
else
fprintf(file, "extern %s;\n", sym->type->GetCDeclaration(sym->name).c_str());
@@ -1625,6 +1647,9 @@ Module::CompileAndOutput(const char *srcFile, const char *arch, const char *cpu,
if (!m->writeOutput(Module::Header, headerFileName))
return 1;
}
else
++m->errorCount;
int errorCount = m->errorCount;
delete m;
m = NULL;

1421
opt.cpp

File diff suppressed because it is too large Load Diff

160
parse.yy
View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2011, Intel Corporation
Copyright (c) 2010-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -224,7 +224,7 @@ struct ForeachDimension {
%type <enumType> enum_specifier
%type <type> specifier_qualifier_list struct_or_union_specifier
%type <type> type_specifier type_name rate_qualified_new_type
%type <type> type_specifier type_name rate_qualified_type_specifier
%type <type> short_vec_specifier
%type <atomicType> atomic_var_type_specifier
@@ -267,25 +267,30 @@ primary_expression
}
}
| TOKEN_INT32_CONSTANT {
$$ = new ConstExpr(AtomicType::UniformConstInt32, (int32_t)yylval.intVal, @1);
$$ = new ConstExpr(AtomicType::UniformInt32->GetAsConstType(),
(int32_t)yylval.intVal, @1);
}
| TOKEN_UINT32_CONSTANT {
$$ = new ConstExpr(AtomicType::UniformConstUInt32, (uint32_t)yylval.intVal, @1);
$$ = new ConstExpr(AtomicType::UniformUInt32->GetAsConstType(),
(uint32_t)yylval.intVal, @1);
}
| TOKEN_INT64_CONSTANT {
$$ = new ConstExpr(AtomicType::UniformConstInt64, (int64_t)yylval.intVal, @1);
$$ = new ConstExpr(AtomicType::UniformInt64->GetAsConstType(),
(int64_t)yylval.intVal, @1);
}
| TOKEN_UINT64_CONSTANT {
$$ = new ConstExpr(AtomicType::UniformConstUInt64, (uint64_t)yylval.intVal, @1);
$$ = new ConstExpr(AtomicType::UniformUInt64->GetAsConstType(),
(uint64_t)yylval.intVal, @1);
}
| TOKEN_FLOAT_CONSTANT {
$$ = new ConstExpr(AtomicType::UniformConstFloat, (float)yylval.floatVal, @1);
$$ = new ConstExpr(AtomicType::UniformFloat->GetAsConstType(),
(float)yylval.floatVal, @1);
}
| TOKEN_TRUE {
$$ = new ConstExpr(AtomicType::UniformConstBool, true, @1);
$$ = new ConstExpr(AtomicType::UniformBool->GetAsConstType(), true, @1);
}
| TOKEN_FALSE {
$$ = new ConstExpr(AtomicType::UniformConstBool, false, @1);
$$ = new ConstExpr(AtomicType::UniformBool->GetAsConstType(), false, @1);
}
| TOKEN_NULL {
$$ = new NullPointerExpr(@1);
@@ -471,25 +476,66 @@ rate_qualified_new
| TOKEN_VARYING TOKEN_NEW { $$ = TYPEQUAL_VARYING; }
;
rate_qualified_new_type
rate_qualified_type_specifier
: type_specifier { $$ = $1; }
| TOKEN_UNIFORM type_specifier { $$ = $2 ? $2->GetAsUniformType() : NULL; }
| TOKEN_VARYING type_specifier { $$ = $2 ? $2->GetAsVaryingType() : NULL; }
| TOKEN_UNIFORM type_specifier
{
if ($2 == NULL)
$$ = NULL;
else if (Type::Equal($2, AtomicType::Void)) {
Error(@1, "\"uniform\" qualifier is illegal with \"void\" type.");
$$ = NULL;
}
else
$$ = $2->GetAsUniformType();
}
| TOKEN_VARYING type_specifier
{
if ($2 == NULL)
$$ = NULL;
else if (Type::Equal($2, AtomicType::Void)) {
Error(@1, "\"varying\" qualifier is illegal with \"void\" type.");
$$ = NULL;
}
else
$$ = $2->GetAsVaryingType();
}
| soa_width_specifier type_specifier
{
if ($2 == NULL)
$$ = NULL;
else {
int soaWidth = $1;
const StructType *st = dynamic_cast<const StructType *>($2);
if (st == NULL) {
Error(@1, "\"soa\" qualifier is illegal with non-struct type \"%s\".",
$2->GetString().c_str());
$$ = NULL;
}
else if (soaWidth <= 0 || (soaWidth & (soaWidth - 1)) != 0) {
Error(@1, "soa<%d> width illegal. Value must be positive power "
"of two.", soaWidth);
$$ = NULL;
}
else
$$ = st->GetAsSOAType(soaWidth);
}
}
;
new_expression
: conditional_expression
| rate_qualified_new rate_qualified_new_type
| rate_qualified_new rate_qualified_type_specifier
{
$$ = new NewExpr($1, $2, NULL, NULL, @1, Union(@1, @2));
$$ = new NewExpr((int32_t)$1, $2, NULL, NULL, @1, Union(@1, @2));
}
| rate_qualified_new rate_qualified_new_type '(' initializer_list ')'
| rate_qualified_new rate_qualified_type_specifier '(' initializer_list ')'
{
$$ = new NewExpr($1, $2, $4, NULL, @1, Union(@1, @2));
$$ = new NewExpr((int32_t)$1, $2, $4, NULL, @1, Union(@1, @2));
}
| rate_qualified_new rate_qualified_new_type '[' expression ']'
| rate_qualified_new rate_qualified_type_specifier '[' expression ']'
{
$$ = new NewExpr($1, $2, NULL, $4, @1, Union(@1, @4));
$$ = new NewExpr((int32_t)$1, $2, NULL, $4, @1, Union(@1, @4));
}
;
@@ -690,13 +736,13 @@ type_specifier
atomic_var_type_specifier
: TOKEN_VOID { $$ = AtomicType::Void; }
| TOKEN_BOOL { $$ = AtomicType::UnboundBool; }
| TOKEN_INT8 { $$ = AtomicType::UnboundInt8; }
| TOKEN_INT16 { $$ = AtomicType::UnboundInt16; }
| TOKEN_INT { $$ = AtomicType::UnboundInt32; }
| TOKEN_FLOAT { $$ = AtomicType::UnboundFloat; }
| TOKEN_DOUBLE { $$ = AtomicType::UnboundDouble; }
| TOKEN_INT64 { $$ = AtomicType::UnboundInt64; }
| TOKEN_BOOL { $$ = AtomicType::UniformBool->GetAsUnboundVariabilityType(); }
| TOKEN_INT8 { $$ = AtomicType::UniformInt8->GetAsUnboundVariabilityType(); }
| TOKEN_INT16 { $$ = AtomicType::UniformInt16->GetAsUnboundVariabilityType(); }
| TOKEN_INT { $$ = AtomicType::UniformInt32->GetAsUnboundVariabilityType(); }
| TOKEN_FLOAT { $$ = AtomicType::UniformFloat->GetAsUnboundVariabilityType(); }
| TOKEN_DOUBLE { $$ = AtomicType::UniformDouble->GetAsUnboundVariabilityType(); }
| TOKEN_INT64 { $$ = AtomicType::UniformInt64->GetAsUnboundVariabilityType(); }
;
short_vec_specifier
@@ -721,7 +767,7 @@ struct_or_union_specifier
GetStructTypesNamesPositions(*$4, &elementTypes, &elementNames,
&elementPositions);
StructType *st = new StructType($2, elementTypes, elementNames,
elementPositions, false, Type::Unbound, @2);
elementPositions, false, Variability::Unbound, @2);
m->symbolTable->AddType($2, st, @2);
$$ = st;
}
@@ -738,7 +784,7 @@ struct_or_union_specifier
&elementPositions);
// FIXME: should be unbound
$$ = new StructType("", elementTypes, elementNames, elementPositions,
false, Type::Unbound, @1);
false, Variability::Unbound, @1);
}
else
$$ = NULL;
@@ -803,16 +849,28 @@ specifier_qualifier_list
| type_qualifier specifier_qualifier_list
{
if ($2 != NULL) {
if ($1 == TYPEQUAL_UNIFORM)
$$ = $2->GetAsUniformType();
else if ($1 == TYPEQUAL_VARYING)
$$ = $2->GetAsVaryingType();
if ($1 == TYPEQUAL_UNIFORM) {
if (Type::Equal($2, AtomicType::Void)) {
Error(@1, "\"uniform\" qualifier is illegal with \"void\" type.");
$$ = NULL;
}
else
$$ = $2->GetAsUniformType();
}
else if ($1 == TYPEQUAL_VARYING) {
if (Type::Equal($2, AtomicType::Void)) {
Error(@1, "\"varying\" qualifier is illegal with \"void\" type.");
$$ = NULL;
}
else
$$ = $2->GetAsVaryingType();
}
else if ($1 == TYPEQUAL_CONST)
$$ = $2->GetAsConstType();
else if ($1 == TYPEQUAL_SIGNED) {
if ($2->IsIntType() == false) {
Error(@1, "Can't apply \"signed\" qualifier to \"%s\" type.",
$2->ResolveUnboundVariability(Type::Varying)->GetString().c_str());
$2->ResolveUnboundVariability(Variability::Varying)->GetString().c_str());
$$ = $2;
}
}
@@ -822,7 +880,7 @@ specifier_qualifier_list
$$ = t;
else {
Error(@1, "Can't apply \"unsigned\" qualifier to \"%s\" type. Ignoring.",
$2->ResolveUnboundVariability(Type::Varying)->GetString().c_str());
$2->ResolveUnboundVariability(Variability::Varying)->GetString().c_str());
$$ = $2;
}
}
@@ -954,7 +1012,7 @@ enumerator
if ($1 != NULL && $3 != NULL &&
lGetConstantInt($3, &value, @3, "Enumerator value")) {
Symbol *sym = new Symbol($1, @1);
sym->constValue = new ConstExpr(AtomicType::UniformConstUInt32,
sym->constValue = new ConstExpr(AtomicType::UniformUInt32->GetAsConstType(),
(uint32_t)value, @3);
$$ = sym;
}
@@ -1459,7 +1517,7 @@ foreach_tiled_scope
foreach_identifier
: TOKEN_IDENTIFIER
{
$$ = new Symbol(yytext, @1, AtomicType::VaryingConstInt32);
$$ = new Symbol(yytext, @1, AtomicType::VaryingInt32->GetAsConstType());
}
;
@@ -1774,11 +1832,17 @@ lAddDeclaration(DeclSpecs *ds, Declarator *decl) {
m->AddTypeDef(decl->GetSymbol());
else {
const Type *t = decl->GetType(ds);
if (t == NULL)
if (t == NULL) {
Assert(m->errorCount > 0);
return;
}
Symbol *sym = decl->GetSymbol();
Assert(sym != NULL);
if (sym == NULL) {
Assert(m->errorCount > 0);
return;
}
const FunctionType *ft = dynamic_cast<const FunctionType *>(t);
if (ft != NULL) {
sym->type = ft;
@@ -1790,7 +1854,7 @@ lAddDeclaration(DeclSpecs *ds, Declarator *decl) {
if (sym->type == NULL)
Assert(m->errorCount > 0);
else
sym->type = sym->type->ResolveUnboundVariability(Type::Varying);
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
bool isConst = (ds->typeQualifiers & TYPEQUAL_CONST) != 0;
m->AddGlobalVariable(sym, decl->initExpr, isConst);
}
@@ -1813,7 +1877,10 @@ lAddFunctionParams(Declarator *decl) {
// walk down to the declarator for the function itself
while (decl->kind != DK_FUNCTION && decl->child != NULL)
decl = decl->child;
Assert(decl->kind == DK_FUNCTION);
if (decl->kind != DK_FUNCTION) {
Assert(m->errorCount > 0);
return;
}
// now loop over its parameters and add them to the symbol table
for (unsigned int i = 0; i < decl->functionParams.size(); ++i) {
@@ -1827,7 +1894,7 @@ lAddFunctionParams(Declarator *decl) {
if (sym == NULL || sym->type == NULL)
Assert(m->errorCount > 0);
else {
sym->type = sym->type->ResolveUnboundVariability(Type::Varying);
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
#ifndef NDEBUG
bool ok = m->symbolTable->AddVariable(sym);
if (ok == false)
@@ -1846,7 +1913,8 @@ lAddFunctionParams(Declarator *decl) {
/** Add a symbol for the built-in mask variable to the symbol table */
static void lAddMaskToSymbolTable(SourcePos pos) {
const Type *t = g->target.maskBitCount == 1 ?
AtomicType::VaryingConstBool : AtomicType::VaryingConstUInt32;
AtomicType::VaryingBool : AtomicType::VaryingUInt32;
t = t->GetAsConstType();
Symbol *maskSymbol = new Symbol("__mask", pos, t);
m->symbolTable->AddVariable(maskSymbol);
}
@@ -1855,16 +1923,18 @@ static void lAddMaskToSymbolTable(SourcePos pos) {
/** Add the thread index and thread count variables to the symbol table
(this should only be done for 'task'-qualified functions. */
static void lAddThreadIndexCountToSymbolTable(SourcePos pos) {
Symbol *threadIndexSym = new Symbol("threadIndex", pos, AtomicType::UniformConstUInt32);
const Type *type = AtomicType::UniformUInt32->GetAsConstType();
Symbol *threadIndexSym = new Symbol("threadIndex", pos, type);
m->symbolTable->AddVariable(threadIndexSym);
Symbol *threadCountSym = new Symbol("threadCount", pos, AtomicType::UniformConstUInt32);
Symbol *threadCountSym = new Symbol("threadCount", pos, type);
m->symbolTable->AddVariable(threadCountSym);
Symbol *taskIndexSym = new Symbol("taskIndex", pos, AtomicType::UniformConstUInt32);
Symbol *taskIndexSym = new Symbol("taskIndex", pos, type);
m->symbolTable->AddVariable(taskIndexSym);
Symbol *taskCountSym = new Symbol("taskCount", pos, AtomicType::UniformConstUInt32);
Symbol *taskCountSym = new Symbol("taskCount", pos, type);
m->symbolTable->AddVariable(taskCountSym);
}

View File

@@ -335,6 +335,131 @@ static inline uniform int lanemask() {
return __movmsk(__mask);
}
///////////////////////////////////////////////////////////////////////////
// memcpy/memmove/memset
static inline void memcpy(void * uniform dst, void * uniform src,
uniform int32 count) {
__memcpy32((int8 * uniform)dst, (int8 * uniform)src, count);
}
static inline void memcpy64(void * uniform dst, void * uniform src,
uniform int64 count) {
__memcpy64((int8 * uniform)dst, (int8 * uniform)src, count);
}
static inline void memcpy(void * varying dst, void * varying src,
int32 count) {
void * uniform da[programCount];
void * uniform sa[programCount];
da[programIndex] = dst;
sa[programIndex] = src;
uniform int mask = lanemask();
for (uniform int i = 0; i < programCount; ++i) {
if ((mask & (1 << i)) == 0)
continue;
void * uniform d = da[i], * uniform s = sa[i];
__memcpy32((int8 * uniform)d, (int8 * uniform)s, extract(count, i));
}
}
static inline void memcpy64(void * varying dst, void * varying src,
int64 count) {
void * uniform da[programCount];
void * uniform sa[programCount];
da[programIndex] = dst;
sa[programIndex] = src;
uniform int mask = lanemask();
for (uniform int i = 0; i < programCount; ++i) {
if ((mask & (1 << i)) == 0)
continue;
void * uniform d = da[i], * uniform s = sa[i];
__memcpy64((int8 * uniform)d, (int8 * uniform)s, extract(count, i));
}
}
static inline void memmove(void * uniform dst, void * uniform src,
uniform int32 count) {
__memmove32((int8 * uniform)dst, (int8 * uniform)src, count);
}
static inline void memmove64(void * uniform dst, void * uniform src,
uniform int64 count) {
__memmove64((int8 * uniform)dst, (int8 * uniform)src, count);
}
static inline void memmove(void * varying dst, void * varying src,
int32 count) {
void * uniform da[programCount];
void * uniform sa[programCount];
da[programIndex] = dst;
sa[programIndex] = src;
uniform int mask = lanemask();
for (uniform int i = 0; i < programCount; ++i) {
if ((mask & (1 << i)) == 0)
continue;
void * uniform d = da[i], * uniform s = sa[i];
__memmove32((int8 * uniform)d, (int8 * uniform)s, extract(count, i));
}
}
static inline void memmove64(void * varying dst, void * varying src,
int64 count) {
void * uniform da[programCount];
void * uniform sa[programCount];
da[programIndex] = dst;
sa[programIndex] = src;
uniform int mask = lanemask();
for (uniform int i = 0; i < programCount; ++i) {
if ((mask & (1 << i)) == 0)
continue;
void * uniform d = da[i], * uniform s = sa[i];
__memmove64((int8 * uniform)d, (int8 * uniform)s, extract(count, i));
}
}
static inline void memset(void * uniform ptr, uniform int8 val,
uniform int32 count) {
__memset32((int8 * uniform)ptr, val, count);
}
static inline void memset64(void * uniform ptr, uniform int8 val,
uniform int64 count) {
__memset64((int8 * uniform)ptr, val, count);
}
static inline void memset(void * varying ptr, int8 val, int32 count) {
void * uniform pa[programCount];
pa[programIndex] = ptr;
uniform int mask = lanemask();
for (uniform int i = 0; i < programCount; ++i) {
if ((mask & (1 << i)) == 0)
continue;
__memset32((int8 * uniform)pa[i], extract(val, i), extract(count, i));
}
}
static inline void memset64(void * varying ptr, int8 val, int64 count) {
void * uniform pa[programCount];
pa[programIndex] = ptr;
uniform int mask = lanemask();
for (uniform int i = 0; i < programCount; ++i) {
if ((mask & (1 << i)) == 0)
continue;
__memset64((int8 * uniform)pa[i], extract(val, i), extract(count, i));
}
}
///////////////////////////////////////////////////////////////////////////
// count leading/trailing zeros
@@ -446,8 +571,8 @@ count_trailing_zeros(int64 v) {
// AOS/SOA conversion
static inline void
aos_to_soa3(uniform float a[], float * uniform v0, float * uniform v1,
float * uniform v2) {
aos_to_soa3(uniform float a[], varying float * uniform v0,
varying float * uniform v1, varying float * uniform v2) {
__aos_to_soa3_float(a, v0, v1, v2);
}
@@ -457,8 +582,9 @@ soa_to_aos3(float v0, float v1, float v2, uniform float a[]) {
}
static inline void
aos_to_soa4(uniform float a[], float * uniform v0, float * uniform v1,
float * uniform v2, float * uniform v3) {
aos_to_soa4(uniform float a[], varying float * uniform v0,
varying float * uniform v1, varying float * uniform v2,
varying float * uniform v3) {
__aos_to_soa4_float(a, v0, v1, v2, v3);
}
@@ -468,10 +594,10 @@ soa_to_aos4(float v0, float v1, float v2, float v3, uniform float a[]) {
}
static inline void
aos_to_soa3(uniform int32 a[], int32 * uniform v0, int32 * uniform v1,
int32 * uniform v2) {
aos_to_soa3((uniform float * uniform)a, (float * uniform)v0,
(float * uniform)v1, (float * uniform)v2);
aos_to_soa3(uniform int32 a[], varying int32 * uniform v0,
varying int32 * uniform v1, varying int32 * uniform v2) {
aos_to_soa3((uniform float * uniform)a, (varying float * uniform)v0,
(varying float * uniform)v1, (varying float * uniform)v2);
}
static inline void
@@ -481,10 +607,12 @@ soa_to_aos3(int32 v0, int32 v1, int32 v2, uniform int32 a[]) {
}
static inline void
aos_to_soa4(uniform int32 a[], int32 * uniform v0, int32 * uniform v1,
int32 * uniform v2, int32 * uniform v3) {
aos_to_soa4((uniform float * uniform)a, (float * uniform )v0,
(float * uniform)v1, (float * uniform)v2, (float * uniform)v3);
aos_to_soa4(uniform int32 a[], varying int32 * uniform v0,
varying int32 * uniform v1, varying int32 * uniform v2,
varying int32 * uniform v3) {
aos_to_soa4((uniform float * uniform)a, (varying float * uniform )v0,
(varying float * uniform)v1, (varying float * uniform)v2,
(varying float * uniform)v3);
}
static inline void
@@ -763,24 +891,24 @@ static unsigned int64 exclusive_scan_or(unsigned int64 v) {
// packed load, store
static inline uniform int
packed_load_active(uniform unsigned int * uniform a,
unsigned int * uniform vals) {
packed_load_active(uniform unsigned int a[],
varying unsigned int * uniform vals) {
return __packed_load_active(a, vals, (UIntMaskType)__mask);
}
static inline uniform int
packed_store_active(uniform unsigned int * uniform a,
packed_store_active(uniform unsigned int a[],
unsigned int vals) {
return __packed_store_active(a, vals, (UIntMaskType)__mask);
}
static inline uniform int
packed_load_active(uniform int * uniform a, int * uniform vals) {
packed_load_active(uniform int a[], varying int * uniform vals) {
return __packed_load_active(a, vals, (IntMaskType)__mask);
}
static inline uniform int
packed_store_active(uniform int * uniform a, int vals) {
packed_store_active(uniform int a[], int vals) {
return __packed_store_active(a, vals, (IntMaskType)__mask);
}
@@ -1630,7 +1758,7 @@ static inline uniform float ldexp(uniform float x, uniform int n) {
return floatbits(ix);
}
static inline float frexp(float x, int * uniform pw2) {
static inline float frexp(float x, varying int * uniform pw2) {
unsigned int ex = 0x7F800000u; // exponent mask
unsigned int ix = intbits(x);
ex &= ix;
@@ -1782,6 +1910,116 @@ static inline uniform float sin(uniform float x_full) {
}
static inline float asin(float x) {
bool isneg = x < 0;
x = abs(x);
bool isnan = (x > 1);
float v;
if (__math_lib == __math_lib_svml ||
__math_lib == __math_lib_system) {
float ret;
uniform int mask = lanemask();
for (uniform int i = 0; i < programCount; ++i) {
if ((mask & (1 << i)) == 0)
continue;
uniform float r = __stdlib_asinf(extract(x, i));
ret = insert(ret, i, r);
}
return ret;
}
else if (__math_lib == __math_lib_ispc)
// sollya
// fpminimax(((asin(x)-pi/2)/-sqrt(1-x)), [|0,1,2,3,4,5,6,7,8,9,10|],
// [|single...|], [1e-20;.9999999999999999]);
// avg error: 8.5716801e-09, max error: 2.1373853e-07
v = 1.57079637050628662109375f +
x * (-0.21460501849651336669921875f +
x * (8.9116774499416351318359375e-2f +
x * (-5.146093666553497314453125e-2f +
x * (3.7269376218318939208984375e-2f +
x * (-3.5882405936717987060546875e-2f +
x * (4.14929799735546112060546875e-2f +
x * (-4.25077490508556365966796875e-2f +
x * (3.05023305118083953857421875e-2f +
x * (-1.2897425331175327301025390625e-2f +
x * 2.38926825113594532012939453125e-3f)))))))));
else if (__math_lib == __math_lib_ispc_fast)
// sollya
// fpminimax(((asin(x)-pi/2)/-sqrt(1-x)), [|0,1,2,3,4,5|],[|single...|],
// [1e-20;.9999999999999999]);
// avg error: 1.1105439e-06, max error 1.3187528e-06
v = 1.57079517841339111328125f +
x * (-0.21450997889041900634765625f +
x * (8.78556668758392333984375e-2f +
x * (-4.489909112453460693359375e-2f +
x * (1.928029954433441162109375e-2f +
x * (-4.3095736764371395111083984375e-3f)))));
v *= -sqrt(1.f - x);
v = v + 1.57079637050628662109375;
if (v < 0) v = 0;
// v = max(0, v);
if (isneg) v = -v;
if (isnan) v = floatbits(0x7fc00000);
return v;
}
static inline uniform float asin(uniform float x) {
uniform bool isneg = x < 0;
x = abs(x);
uniform bool isnan = (x > 1);
uniform float v;
if (__math_lib == __math_lib_svml ||
__math_lib == __math_lib_system) {
return __stdlib_asinf(x);
}
else if (__math_lib == __math_lib_ispc)
// sollya
// fpminimax(((asin(x)-pi/2)/-sqrt(1-x)), [|0,1,2,3,4,5,6,7,8,9,10|],
// [|single...|], [1e-20;.9999999999999999]);
// avg error: 8.5716801e-09, max error: 2.1373853e-07
v = 1.57079637050628662109375f +
x * (-0.21460501849651336669921875f +
x * (8.9116774499416351318359375e-2f +
x * (-5.146093666553497314453125e-2f +
x * (3.7269376218318939208984375e-2f +
x * (-3.5882405936717987060546875e-2f +
x * (4.14929799735546112060546875e-2f +
x * (-4.25077490508556365966796875e-2f +
x * (3.05023305118083953857421875e-2f +
x * (-1.2897425331175327301025390625e-2f +
x * 2.38926825113594532012939453125e-3f)))))))));
else if (__math_lib == __math_lib_ispc_fast)
// sollya
// fpminimax(((asin(x)-pi/2)/-sqrt(1-x)), [|0,1,2,3,4,5|],[|single...|],
// [1e-20;.9999999999999999]);
// avg error: 1.1105439e-06, max error 1.3187528e-06
v = 1.57079517841339111328125f +
x * (-0.21450997889041900634765625f +
x * (8.78556668758392333984375e-2f +
x * (-4.489909112453460693359375e-2f +
x * (1.928029954433441162109375e-2f +
x * (-4.3095736764371395111083984375e-3f)))));
v *= -sqrt(1.f - x);
v = v + 1.57079637050628662109375;
if (v < 0) v = 0;
// v = max(0, v);
if (isneg) v = -v;
if (isnan) v = floatbits(0x7fc00000);
return v;
}
static inline float cos(float x_full) {
if (__math_lib == __math_lib_svml) {
return __svml_cos(x_full);
@@ -1909,8 +2147,18 @@ static inline uniform float cos(uniform float x_full) {
}
static inline void sincos(float x_full, float * uniform sin_result,
float * uniform cos_result) {
static inline float acos(float v) {
return 1.57079637050628662109375 - asin(v);
}
static inline uniform float acos(uniform float v) {
return 1.57079637050628662109375 - asin(v);
}
static inline void sincos(float x_full, varying float * uniform sin_result,
varying float * uniform cos_result) {
if (__math_lib == __math_lib_svml) {
__svml_sincos(x_full, sin_result, cos_result);
}
@@ -2507,8 +2755,8 @@ static inline uniform float exp(uniform float x_full) {
// Range reduction for logarithms takes log(x) -> log(2^n * y) -> n
// * log(2) + log(y) where y is the reduced range (usually in [1/2,
// 1)).
static inline void __range_reduce_log(float input, float * uniform reduced,
int * uniform exponent) {
static inline void __range_reduce_log(float input, varying float * uniform reduced,
varying int * uniform exponent) {
int int_version = intbits(input);
// single precision = SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
// exponent mask = 0111 1111 1000 0000 0000 0000 0000 0000
@@ -2785,7 +3033,7 @@ static inline uniform double ldexp(uniform double x, uniform int n) {
return doublebits(ix);
}
static inline double frexp(double x, int * uniform pw2) {
static inline double frexp(double x, varying int * uniform pw2) {
unsigned int64 ex = 0x7ff0000000000000; // exponent mask
unsigned int64 ix = intbits(x);
ex &= ix;
@@ -2851,8 +3099,8 @@ static inline uniform double cos(uniform double x) {
return __stdlib_cos(x);
}
static inline void sincos(double x, double * uniform sin_result,
double * uniform cos_result) {
static inline void sincos(double x, varying double * uniform sin_result,
varying double * uniform cos_result) {
if (__math_lib == __math_lib_ispc_fast) {
float sr, cr;
sincos((float)x, &sr, &cr);
@@ -3391,7 +3639,7 @@ struct RNGState {
unsigned int z1, z2, z3, z4;
};
static inline unsigned int random(RNGState * uniform state)
static inline unsigned int random(varying RNGState * uniform state)
{
unsigned int b;
@@ -3406,14 +3654,36 @@ static inline unsigned int random(RNGState * uniform state)
return (state->z1 ^ state->z2 ^ state->z3 ^ state->z4);
}
static inline float frandom(RNGState * uniform state)
static inline uniform unsigned int random(uniform RNGState * uniform state)
{
uniform unsigned int b;
b = ((state->z1 << 6) ^ state->z1) >> 13;
state->z1 = ((state->z1 & 4294967294U) << 18) ^ b;
b = ((state->z2 << 2) ^ state->z2) >> 27;
state->z2 = ((state->z2 & 4294967288U) << 2) ^ b;
b = ((state->z3 << 13) ^ state->z3) >> 21;
state->z3 = ((state->z3 & 4294967280U) << 7) ^ b;
b = ((state->z4 << 3) ^ state->z4) >> 12;
state->z4 = ((state->z4 & 4294967168U) << 13) ^ b;
return (state->z1 ^ state->z2 ^ state->z3 ^ state->z4);
}
static inline float frandom(varying RNGState * uniform state)
{
unsigned int irand = random(state);
irand &= (1<<23)-1;
return floatbits(0x3F800000 | irand)-1.0f;
}
static inline uniform unsigned int __seed4(RNGState * uniform state,
static inline uniform float frandom(uniform RNGState * uniform state)
{
uniform unsigned int irand = random(state);
irand &= (1<<23)-1;
return floatbits(0x3F800000 | irand)-1.0f;
}
static inline uniform unsigned int __seed4(varying RNGState * uniform state,
uniform int start,
uniform unsigned int seed) {
uniform unsigned int c1 = 0xf0f0f0f0;
@@ -3447,7 +3717,8 @@ static inline uniform unsigned int __seed4(RNGState * uniform state,
return seed;
}
static inline void seed_rng(uniform RNGState * uniform state, uniform unsigned int seed) {
static inline void seed_rng(varying RNGState * uniform state,
uniform unsigned int seed) {
if (programCount == 1) {
state->z1 = seed;
state->z2 = seed ^ 0xbeeff00d;
@@ -3468,6 +3739,16 @@ static inline void seed_rng(uniform RNGState * uniform state, uniform unsigned i
}
}
static inline void seed_rng(uniform RNGState * uniform state,
uniform unsigned int seed) {
state->z1 = seed;
state->z2 = seed ^ 0xbeeff00d;
state->z3 = ((seed & 0xffff) << 16) | (seed >> 16);
state->z4 = (((seed & 0xff) << 24) | ((seed & 0xff00) << 8) |
((seed & 0xff0000) >> 8) | (seed & 0xff000000) >> 24);
}
static inline void fastmath() {
__fastmath();
}

View File

@@ -2124,10 +2124,10 @@ SwitchStmt::TypeCheck() {
const Type *toType = NULL;
exprType = exprType->GetAsConstType();
bool is64bit = (exprType->GetAsUniformType() ==
AtomicType::UniformConstUInt64 ||
exprType->GetAsUniformType() ==
AtomicType::UniformConstInt64);
bool is64bit = (Type::EqualIgnoringConst(exprType->GetAsUniformType(),
AtomicType::UniformUInt64) ||
Type::EqualIgnoringConst(exprType->GetAsUniformType(),
AtomicType::UniformInt64));
if (exprType->IsUniformType()) {
if (is64bit) toType = AtomicType::UniformInt64;
@@ -2381,20 +2381,20 @@ PrintStmt::PrintStmt(const std::string &f, Expr *v, SourcePos p)
*/
static char
lEncodeType(const Type *t) {
if (t == AtomicType::UniformBool) return 'b';
if (t == AtomicType::VaryingBool) return 'B';
if (t == AtomicType::UniformInt32) return 'i';
if (t == AtomicType::VaryingInt32) return 'I';
if (t == AtomicType::UniformUInt32) return 'u';
if (t == AtomicType::VaryingUInt32) return 'U';
if (t == AtomicType::UniformFloat) return 'f';
if (t == AtomicType::VaryingFloat) return 'F';
if (t == AtomicType::UniformInt64) return 'l';
if (t == AtomicType::VaryingInt64) return 'L';
if (t == AtomicType::UniformUInt64) return 'v';
if (t == AtomicType::VaryingUInt64) return 'V';
if (t == AtomicType::UniformDouble) return 'd';
if (t == AtomicType::VaryingDouble) return 'D';
if (Type::Equal(t, AtomicType::UniformBool)) return 'b';
if (Type::Equal(t, AtomicType::VaryingBool)) return 'B';
if (Type::Equal(t, AtomicType::UniformInt32)) return 'i';
if (Type::Equal(t, AtomicType::VaryingInt32)) return 'I';
if (Type::Equal(t, AtomicType::UniformUInt32)) return 'u';
if (Type::Equal(t, AtomicType::VaryingUInt32)) return 'U';
if (Type::Equal(t, AtomicType::UniformFloat)) return 'f';
if (Type::Equal(t, AtomicType::VaryingFloat)) return 'F';
if (Type::Equal(t, AtomicType::UniformInt64)) return 'l';
if (Type::Equal(t, AtomicType::VaryingInt64)) return 'L';
if (Type::Equal(t, AtomicType::UniformUInt64)) return 'v';
if (Type::Equal(t, AtomicType::VaryingUInt64)) return 'V';
if (Type::Equal(t, AtomicType::UniformDouble)) return 'd';
if (Type::Equal(t, AtomicType::VaryingDouble)) return 'D';
if (dynamic_cast<const PointerType *>(t) != NULL) {
if (t->IsUniformType())
return 'p';
@@ -2424,10 +2424,10 @@ lProcessPrintArg(Expr *expr, FunctionEmitContext *ctx, std::string &argTypes) {
// Just int8 and int16 types to int32s...
const Type *baseType = type->GetAsNonConstType()->GetAsUniformType();
if (baseType == AtomicType::UniformInt8 ||
baseType == AtomicType::UniformUInt8 ||
baseType == AtomicType::UniformInt16 ||
baseType == AtomicType::UniformUInt16) {
if (Type::Equal(baseType, AtomicType::UniformInt8) ||
Type::Equal(baseType, AtomicType::UniformUInt8) ||
Type::Equal(baseType, AtomicType::UniformInt16) ||
Type::Equal(baseType, AtomicType::UniformUInt16)) {
expr = new TypeCastExpr(type->IsUniformType() ? AtomicType::UniformInt32 :
AtomicType::VaryingInt32,
expr, expr->pos);

23
tests/acos.ispc Normal file
View File

@@ -0,0 +1,23 @@
export uniform int width() { return programCount; }
bool ok(float x, float ref) { return (abs(x - ref) < 1e-6) || abs((x-ref)/ref) < 1e-6; }
export void f_v(uniform float RET[]) {
uniform float vals[8] = { 0, 1, 0.5, -1, -.87, -.25, 1e-3, -.99999999 };
uniform float r[8];
foreach (i = 0 ... 8)
r[i] = cos(acos(vals[i]));
int errors = 0;
for (uniform int i = 0; i < 8; ++i) {
if (ok(r[i], vals[i]) == false) {
print("error @ %: got %, expected %\n", i, r[i], vals[i]);
++errors;
}
}
RET[programIndex] = errors;
}
export void result(uniform float RET[]) { RET[programIndex] = 0; }

23
tests/asin.ispc Normal file
View File

@@ -0,0 +1,23 @@
export uniform int width() { return programCount; }
bool ok(float x, float ref) { return (abs(x - ref) < 1e-6) || abs((x-ref)/ref) < 1e-6; }
export void f_v(uniform float RET[]) {
uniform float vals[8] = { 0, 1, 0.5, -1, -.87, -.25, 1e-3, -.99999999 };
uniform float r[8];
foreach (i = 0 ... 8)
r[i] = sin(asin(vals[i]));
int errors = 0;
for (uniform int i = 0; i < 8; ++i) {
if (ok(r[i], vals[i]) == false) {
print("error @ %: got %, expected %\n", i, r[i], vals[i]);
++errors;
}
}
RET[programIndex] = errors;
}
export void result(uniform float RET[]) { RET[programIndex] = 0; }

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
void foo(float * uniform a) {
void foo(varying float * uniform a) {
*a = 0;
}

View File

@@ -7,13 +7,13 @@ struct Foo {
float f;
};
float func(uniform Foo foo[], int offset) {
float func(Foo foo[], int offset) {
return foo[offset].f;
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
uniform Foo foo[17];
Foo foo[17];
uniform int i;
cfor (i = 0; i < 17; ++i)
foo[i].f = i*a;

View File

@@ -7,13 +7,13 @@ struct Foo {
float f;
};
float func(uniform Foo foo[], int offset) {
float func(Foo foo[], int offset) {
return foo[offset].f;
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
uniform Foo foo[17];
Foo foo[17];
uniform int i;
cfor (i = 0; i < 17; ++i)
foo[i].f = i*a;

View File

@@ -9,7 +9,7 @@ struct Foo {
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
uniform Foo foo[17];
Foo foo[17];
uniform int i;
cfor (i = 0; i < 17; ++i)
foo[i].f = i*a;

14
tests/coalesce-1.ispc Normal file
View File

@@ -0,0 +1,14 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * uniform buf = uniform new uniform float[32*32];
for (uniform int i = 0; i < 32*32; ++i)
buf[i] = i;
RET[programIndex] = buf[64-programIndex];
}
export void result(uniform float RET[]) {
RET[programIndex] = 64 - programIndex;
}

14
tests/coalesce-2.ispc Normal file
View File

@@ -0,0 +1,14 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * uniform buf = uniform new uniform float[32*32];
for (uniform int i = 0; i < 32*32; ++i)
buf[i] = i;
RET[programIndex] = buf[programIndex & 1];
}
export void result(uniform float RET[]) {
RET[programIndex] = programIndex & 1;
}

14
tests/coalesce-3.ispc Normal file
View File

@@ -0,0 +1,14 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * uniform buf = uniform new uniform float[32*32];
for (uniform int i = 0; i < 32*32; ++i)
buf[i] = i;
RET[programIndex] = buf[(programIndex >> 2) * 16 + (programIndex & 3)];
}
export void result(uniform float RET[]) {
RET[programIndex] = (programIndex >> 2) * 16 + (programIndex & 3);
}

17
tests/coalesce-4.ispc Normal file
View File

@@ -0,0 +1,17 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * uniform buf = uniform new uniform float[32*32];
for (uniform int i = 0; i < 32*32; ++i)
buf[i] = i;
float a = buf[2*programIndex];
float b = buf[2*programIndex+1];
RET[programIndex] = a+b;
}
export void result(uniform float RET[]) {
RET[programIndex] = 2 * programIndex + 2 * programIndex + 1;
}

20
tests/coalesce-5.ispc Normal file
View File

@@ -0,0 +1,20 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * uniform buf = uniform new uniform float[32*32];
for (uniform int i = 0; i < 32*32; ++i)
buf[i] = i;
float a = buf[4*programIndex];
float b = buf[4*programIndex+1];
float c = buf[4*programIndex+2];
float d = buf[4*programIndex+3];
RET[programIndex] = a+b+c+d;;
}
export void result(uniform float RET[]) {
RET[programIndex] = 4 * programIndex + 4 * programIndex + 1 +
4 * programIndex + 2 + 4 * programIndex + 3;
}

21
tests/coalesce-6.ispc Normal file
View File

@@ -0,0 +1,21 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * uniform buf = uniform new uniform float[32*32];
for (uniform int i = 0; i < 32*32; ++i)
buf[i] = i;
float a = buf[4*programIndex];
float b = buf[4*programIndex+1];
buf[4*programIndex+2] = 0;
float c = buf[4*programIndex+2];
float d = buf[4*programIndex+3];
RET[programIndex] = a+b+c+d;;
}
export void result(uniform float RET[]) {
RET[programIndex] = 4 * programIndex + 4 * programIndex + 1 +
4 * programIndex + 3;
}

21
tests/coalesce-7.ispc Normal file
View File

@@ -0,0 +1,21 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * uniform buf = uniform new uniform float[32*32];
for (uniform int i = 0; i < 32*32; ++i)
buf[i] = i;
float a = buf[4*programIndex];
buf[4*programIndex+1] = 0;
buf[4*programIndex+3] = 0;
float b = buf[4*programIndex+1];
float c = buf[4*programIndex+2];
float d = buf[4*programIndex+3];
RET[programIndex] = a+b+c+d;;
}
export void result(uniform float RET[]) {
RET[programIndex] = 4 * programIndex + 4 * programIndex + 2;
}

19
tests/coalesce-8.ispc Normal file
View File

@@ -0,0 +1,19 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float * uniform buf = uniform new uniform float[32*32];
for (uniform int i = 0; i < 32*32; ++i)
buf[i] = i;
int index = (programIndex < 4) ? (programIndex & 1) :
(programIndex / 4);
float a = buf[index];
RET[programIndex] = a;
}
export void result(uniform float RET[]) {
RET[programIndex] = (programIndex < 4) ? (programIndex & 1) :
(programIndex / 4);
}

View File

@@ -14,7 +14,7 @@ export void f_f(uniform float RET[], uniform float aFOO[]) {
r[i].v.z = 300*i + 3*programIndex;
}
Ray *rp = &r[programIndex/2];
varying Ray *rp = &r[programIndex/2];
RET[programIndex] = rp->v.z;
}

View File

@@ -1,13 +1,15 @@
export uniform int width() { return programCount; }
struct Foo { uniform float x; float y; };
struct Foo { float x; float y; };
export void f_fu(uniform float ret[], uniform float aa[], uniform float b) {
float a = aa[programIndex];
Foo foo[32];
for (uniform int i = 0; i < 32; ++i)
uniform Foo foo[32];
for (uniform int i = 0; i < 32; ++i) {
foo[i].x = i;
foo[i].y = -1234 + i;
}
varying Foo fv = foo[a];
fv.x += 1000;
//CO print("fv.x = %\n", fv.x);

17
tests/memcpy-uniform.ispc Normal file
View File

@@ -0,0 +1,17 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int32 * uniform src = uniform new int32[1024];
int32 * uniform dst = uniform new int32[1024];
foreach (i = 0 ... 1024)
src[i] = i;
memcpy(&dst[32], src, (1024-32)*sizeof(uniform int));
RET[programIndex] = dst[64+programIndex];
}
export void result(uniform float RET[]) {
RET[programIndex] = 32 + programIndex;
}

21
tests/memcpy-varying.ispc Normal file
View File

@@ -0,0 +1,21 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int32 *src = new int32[1024];
int32 *dst = new int32[1024];
for (uniform int i = 0; i < 1024; ++i)
src[i] = programIndex * 10000 + i;
if (programIndex == 2)
memcpy(dst, src, programCount*sizeof(uniform int));
else
memcpy(dst, src, programCount*sizeof(uniform int));
RET[programIndex] = dst[programIndex];
}
export void result(uniform float RET[]) {
RET[programIndex] = 10000 * programIndex + programIndex;
}

View File

@@ -0,0 +1,16 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int32 * uniform buf = uniform new int32[1024];
foreach (i = 0 ... 1024)
buf[i] = i;
memmove(&buf[1], buf, (1024-1)*sizeof(uniform int));
RET[programIndex] = buf[programIndex];
}
export void result(uniform float RET[]) {
RET[programIndex] = max(0, programIndex-1);
}

View File

@@ -0,0 +1,19 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int32 *buf = new int32[1024];
for (uniform int i = 0; i < 1024; ++i)
buf[i] = programIndex * 10000 + i;
if (programIndex == 2)
memmove(buf, buf+programCount/2, programCount*sizeof(uniform int));
RET[programIndex] = buf[0];
}
export void result(uniform float RET[]) {
RET[programIndex] = 10000 * programIndex;
RET[2] = 10000 * 2 + programCount/2;
}

16
tests/memset-uniform.ispc Normal file
View File

@@ -0,0 +1,16 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int32 * uniform buf = uniform new int32[1024];
buf[0] = 0;
memset(buf+1, 0x7f, 1024*sizeof(uniform int32));
int v = buf[programIndex];
RET[programIndex] = (v == 0x7f7f7f7f);
}
export void result(uniform float RET[]) {
RET[programIndex] = 1;
RET[0] = 0;
}

21
tests/memset-varying.ispc Normal file
View File

@@ -0,0 +1,21 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int32 * varying buf = varying new int32[1024*(programIndex+1)];
if (programIndex & 1) {
memset(buf, 0xff, 1024*(programIndex+1)*sizeof(uniform int32));
}
else {
memset(buf, 0x01, 1024*(programIndex+1)*sizeof(uniform int32));
}
int v = buf[0];
int expected = (programIndex & 1) ? 0xffffffff : 0x01010101;
RET[programIndex] = (v == expected);
}
export void result(uniform float RET[]) {
RET[programIndex] = 1;
}

View File

@@ -4,7 +4,7 @@ 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];
varying float * uniform buf = uniform new varying float[programCount+1];
for (uniform int i = 0; i < programCount+1; ++i) {
buf[i] = i+a;
}

View File

@@ -2,7 +2,7 @@
export uniform int width() { return programCount; }
struct Point {
uniform float x, y, z;
float x, y, z;
};
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {

View File

@@ -7,7 +7,7 @@ struct Point {
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);
varying Point * buf = new varying Point(0., b, a);
RET[programIndex] = buf->z;
delete buf;
}

View File

@@ -3,9 +3,9 @@ export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
float a = aFOO[programIndex];
float * uniform pa = &a;
int * uniform pb = (int *)pa;
float *uniform pc = (float *)pb;
varying float * uniform pa = &a;
varying int * uniform pb = (varying int *)pa;
varying float *uniform pc = (varying float *)pb;
*pc = programIndex;
RET[programIndex] = *pc;
}

View File

@@ -4,7 +4,7 @@ export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int a = aFOO[programIndex];
int * uniform b = &a;
varying int * uniform b = &a;
RET[programIndex] = *b;
}

View File

@@ -6,7 +6,7 @@ struct Foo {
uniform float b;
};
void update(Foo * uniform fp) {
void update(varying Foo * uniform fp) {
fp->a += 1;
fp->b = 1;
}

View File

@@ -6,7 +6,7 @@ struct Foo {
uniform float b;
};
void update(Foo * varying fp) {
void update(varying Foo * varying fp) {
++fp;
fp->a -= 1;
fp->b = 1;

View File

@@ -6,7 +6,7 @@ struct Foo {
uniform float b;
};
void update(float<3> * uniform vp) {
void update(varying float<3> * uniform vp) {
vp->x = 0;
}

View File

@@ -1,7 +1,7 @@
export uniform int width() { return programCount; }
void update(float<2> * varying vp) {
void update(varying float<2> * vp) {
vp->y = 0;
}

View File

@@ -4,7 +4,7 @@ export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int a = aFOO[programIndex];
int * uniform b = &a;
varying int * uniform b = &a;
*b = 2;
RET[programIndex] = *b;
}

View File

@@ -4,7 +4,7 @@ export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int a = aFOO[programIndex];
int * uniform b = &a;
varying int * uniform b = &a;
++*b;
RET[programIndex] = *b;
}

View File

@@ -4,7 +4,7 @@ export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
int a = aFOO[programIndex];
int * uniform b = &a;
varying int * uniform b = &a;
(*b)++;
RET[programIndex] = *b;
}

View File

@@ -1,13 +1,13 @@
export uniform int width() { return programCount; }
void inc(int * uniform v) {
void inc(varying int * uniform v) {
++*v;
}
export void f_f(uniform float RET[], uniform float aFOO[]) {
int a = aFOO[programIndex];
int * uniform b = &a;
varying int * uniform b = &a;
if (a <= 2)
inc(b);
RET[programIndex] = a;

View File

@@ -1,15 +1,15 @@
export uniform int width() { return programCount; }
void inc(int * uniform v) {
void inc(varying int * uniform v) {
++*v;
}
export void f_f(uniform float RET[], uniform float aFOO[]) {
int a = aFOO[programIndex];
int * uniform b = &a;
varying int * uniform b = &a;
void * uniform vp = b;
int * uniform c = (int * uniform)vp;
varying int * uniform c = (varying int * uniform)vp;
RET[programIndex] = *c;
}

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
float foo(float * uniform a) {
float foo(varying float * uniform a) {
*a = 0;
}

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
float foo(float * uniform a) {
float foo(varying float * uniform a) {
*a = 0;
}

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
void foo(float * uniform a) {
void foo(varying float * uniform a) {
*a = 0;
}

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
void foo(float * uniform a) {
void foo(varying float * uniform a) {
*a = 0;
}

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
void foo(float * uniform a) {
void foo(varying float * uniform a) {
*a = 0;
}

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
void foo(float * uniform x, int y) {
void foo(varying float * uniform x, int y) {
*x = y;
}

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
void foo(float * uniform x, float y) {
void foo(varying float * uniform x, float y) {
*x = y;
}

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
void foo(float * uniform x) {
void foo(varying float * uniform x) {
if ((*x) <= 2)
++(*x);
}

View File

@@ -0,0 +1,14 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform float buf[programCount];
uniform float * varying ptr = &buf[programCount - 1 - programIndex];
ptr[0] = programIndex;
RET[programIndex] = buf[programIndex];
}
export void result(uniform float RET[]) {
RET[programIndex] = programCount - 1 - programIndex;
}

View File

@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
float foo(float * uniform a) {
float foo(varying float * uniform a) {
*a = 0;
}

View File

@@ -0,0 +1,30 @@
struct Point { float x, y[3], z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
//CO soa<8> Point pts[10];
uniform Point pts[80];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y[0] = 2*b*i;
pts[i].y[1] = 2*b*i+1;
pts[i].y[2] = 2*b*i+2;
pts[i].z = 3*b*i;
}
a *= -1;
Point vp = { a, { 2*a, 3*a, 4*a }, {5*a} };
pts[2+programIndex] = vp;
RET[programIndex] = pts[programIndex].y[2];
}
export void result(uniform float RET[]) {
RET[programIndex] = -4 * (programIndex-1);
RET[0] = 2;
RET[1] = 12;
}

View File

@@ -3,7 +3,7 @@ typedef int<4> int4;
export uniform int width() { return programCount; }
void inc(int4 * uniform v) {
void inc(varying int4 * uniform v) {
int4 delta = { 1, 1, 1, 1 };
(*v) += delta;
}

View File

@@ -3,7 +3,7 @@ typedef int<4> int4;
export uniform int width() { return programCount; }
void incXY(int4 * uniform v) {
void incXY(varying int4 * uniform v) {
++(*v).x;
++(*v).y;
}

21
tests/soa-1.ispc Normal file
View File

@@ -0,0 +1,21 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
for (uniform int i = 0; i < 8*10; ++i) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
RET[programIndex] = pts[programIndex].y;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10*programIndex;
}

23
tests/soa-10.ispc Normal file
View File

@@ -0,0 +1,23 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
uniform Point up = pts[1];
RET[programIndex] = up.y;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10;
}

15
tests/soa-11.ispc Normal file
View File

@@ -0,0 +1,15 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
soa<4> Point pts[2] = { { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
{ { 13, 14, 15, 16 }, { 17, 18, 19, 20, }, { 21, 22, 23, 24 } } };
RET[programIndex] = pts[1].y;
}
export void result(uniform float RET[]) {
RET[programIndex] = 6;
}

17
tests/soa-12.ispc Normal file
View File

@@ -0,0 +1,17 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<4> Point pts[2] = { { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
{ { 13, 14, 15, 16 }, { 17, 18, 19, 20, }, { 21, 22, 23, 24 } } };
RET[programIndex] = pts[programIndex & 1].y;
}
export void result(uniform float RET[]) {
RET[programIndex] = (programIndex & 1) ? 6 : 5;
}

26
tests/soa-13.ispc Normal file
View File

@@ -0,0 +1,26 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
uniform Point up = { b, 3, 170 };
pts[(int64)1] = up;
RET[programIndex] = pts[(int64)programIndex].z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 15*programIndex;
RET[1] = 170;
}

57
tests/soa-14.ispc Normal file
View File

@@ -0,0 +1,57 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
static void p(uniform float *uniform ptr) {
//CO for (uniform int s = 0; s < 1; ++s) { // num to print
//CO for (uniform int i = 0; i < 3; ++i) { // num float in unif struct
//CO for (uniform int j = 0; j < 8; ++j, ++ptr) // soa width
//CO print("% ", *ptr);
//CO print("\n");
//CO }
//CO print("\n");
//CO }
}
soa<8> Point * uniform aossoa(uniform Point aospts[], uniform int count) {
uniform int roundUp = (count + 7) & ~0x7;
uniform int nAlloc = roundUp / 8;
soa<8> Point * uniform ret = uniform new soa<8> Point[nAlloc];
foreach (i = 0 ... count) {
//CO varying Point gp = { programIndex+1, 2*programIndex+1, 3*programIndex+1 };
//CO ret[i] = gp;
//CO ret[i].x = gp.x;
//CO ret[i].y = gp.y;
//CO ret[i].z = gp.z;
//CO print("%: % % %\n", i, gp.x, gp.y, gp.z);
ret[i] = aospts[i];
}
//CO p((uniform float * uniform)aospts);
//CO print("----\n");
//CO p((uniform float * uniform)ret);
return ret;
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
uniform Point pts[programCount+4];
foreach (i = 0 ... programCount+4) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
soa<8> Point * uniform soaPts = aossoa(pts, programCount+4);
RET[programIndex] = soaPts[programIndex+3].z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 15*(programIndex+3);
}

48
tests/soa-15.ispc Normal file
View File

@@ -0,0 +1,48 @@
struct Point { float x, y[3], z; };
export uniform int width() { return programCount; }
static void p(uniform float *uniform ptr) {
for (uniform int s = 0; s < 4; ++s) {
for (uniform int i = 0; i < 5; ++i) {
for (uniform int j = 0; j < 4; ++j, ++ptr)
print("% ", *ptr);
print("\n");
}
print("\n");
}
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
soa<4> Point pts[10];
//CO uniform Point pts[40];
//CO foreach (i = 0 ... 40) {
for (uniform int i = 0; i < 40; ++i) {
pts[i].x = b*i;
pts[i].y[0] = 2*b*i;
pts[i].y[1] = 2*b*i+1;
pts[i].y[2] = 2*b*i+2;
pts[i].z = 3*b*i;
}
//CO p((uniform float * uniform)&pts[0]);
//CO print("delta %\n", ((uniform float * varying)(&pts[2+programIndex]) -
//CO (uniform float * uniform)&pts[0]));
float a = aFOO[programIndex];
a *= -1;
Point vp = { a, { 2*a, 3*a, 4*a }, {5*a} };
pts[2+programIndex] = vp;
//CO p((uniform float * uniform)&pts[0]);
RET[programIndex] = pts[programIndex].y[2];
}
export void result(uniform float RET[]) {
RET[programIndex] = -4 * (programIndex-1);
RET[0] = 2;
RET[1] = 12;
}

48
tests/soa-16.ispc Normal file
View File

@@ -0,0 +1,48 @@
struct Point { double x; double y[3], z; };
export uniform int width() { return programCount; }
static void p(uniform float *uniform ptr) {
for (uniform int s = 0; s < 4; ++s) {
for (uniform int i = 0; i < 5; ++i) {
for (uniform int j = 0; j < 4; ++j, ++ptr)
print("% ", *ptr);
print("\n");
}
print("\n");
}
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
soa<4> Point pts[10];
//CO uniform Point pts[40];
//CO foreach (i = 0 ... 40) {
for (uniform int i = 0; i < 40; ++i) {
pts[i].x = b*i;
pts[i].y[0] = 2*b*i;
pts[i].y[1] = 2*b*i+1;
pts[i].y[2] = 2*b*i+2;
pts[i].z = 3*b*i;
}
//CO p((uniform float * uniform)&pts[0]);
//CO print("delta %\n", ((uniform float * varying)(&pts[2+programIndex]) -
//CO (uniform float * uniform)&pts[0]));
float a = aFOO[programIndex];
a *= -1;
Point vp = { a, { 2*a, 3*a, 4*a }, {5*a} };
pts[2+programIndex] = vp;
//CO p((uniform float * uniform)&pts[0]);
RET[programIndex] = pts[programIndex].y[2];
}
export void result(uniform float RET[]) {
RET[programIndex] = -4 * (programIndex-1);
RET[0] = 2;
RET[1] = 12;
}

50
tests/soa-17.ispc Normal file
View File

@@ -0,0 +1,50 @@
struct Point { double x; float y[3], z; };
export uniform int width() { return programCount; }
static void p(uniform float *uniform ptr) {
for (uniform int s = 0; s < 4; ++s) {
for (uniform int i = 0; i < 5; ++i) {
for (uniform int j = 0; j < 4; ++j, ++ptr)
print("% ", *ptr);
print("\n");
}
print("\n");
}
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
soa<4> Point pts[10];
//CO uniform Point pts[40];
//CO foreach (i = 0 ... 40) {
for (uniform int i = 0; i < 40; ++i) {
pts[i].x = b*i;
pts[i].y[0] = 2*b*i;
pts[i].y[1] = 2*b*i+1;
pts[i].y[2] = 2*b*i+2;
pts[i].z = 3*b*i;
}
//CO p((uniform float * uniform)&pts[0]);
//CO print("one size %\n", sizeof(soa<4> Point));
//CO print("delta %\n", ((uniform int8 * varying)(&pts[2+programIndex]) -
//CO (uniform int8 * uniform)&pts[0]));
float a = aFOO[programIndex];
a *= -1;
Point vp = { a, { 2*a, 3*a, 4*a }, {5*a} };
pts[2+programIndex] = vp;
//CO p((uniform float * uniform)&pts[0]);
RET[programIndex] = pts[programIndex].y[2];
}
export void result(uniform float RET[]) {
RET[programIndex] = -4 * (programIndex-1);
RET[0] = 2;
RET[1] = 12;
}

25
tests/soa-18.ispc Normal file
View File

@@ -0,0 +1,25 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
soa<8> Point * ptr = &pts[programIndex];
++ptr;
ptr->y = -programIndex;
RET[programIndex] = pts[1+programIndex].y;
}
export void result(uniform float RET[]) {
RET[programIndex] = -programIndex;
}

Some files were not shown because too many files have changed in this diff Show More