Merge branch 'master' of git://github.com/ispc/ispc
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -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/*
|
||||
|
||||
|
||||
|
||||
3
Makefile
3
Makefile
@@ -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
|
||||
|
||||
18
builtins.cpp
18
builtins.cpp
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
282
cbackend.cpp
282
cbackend.cpp
@@ -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
698
ctx.cpp
@@ -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
39
ctx.h
@@ -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
136
decl.cpp
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
45
docs/faq.rst
45
docs/faq.rst
@@ -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
|
||||
======================
|
||||
|
||||
|
||||
567
docs/ispc.rst
567
docs/ispc.rst
@@ -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
28
docs/news.rst
Normal 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.
|
||||
@@ -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
65
docs/template-news.txt
Normal 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"> © 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
|
||||
@@ -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"> © 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"> © 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>
|
||||
|
||||
@@ -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"> © 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"> © 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>
|
||||
|
||||
@@ -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
|
||||
==
|
||||
|
||||
|
||||
@@ -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 } };
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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[]
|
||||
|
||||
@@ -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
|
||||
|
||||
7
examples/perfbench/Makefile
Normal file
7
examples/perfbench/Makefile
Normal 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
|
||||
108
examples/perfbench/perfbench.cpp
Normal file
108
examples/perfbench/perfbench.cpp
Normal 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;
|
||||
}
|
||||
|
||||
170
examples/perfbench/perfbench.ispc
Normal file
170
examples/perfbench/perfbench.ispc
Normal 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;
|
||||
}
|
||||
}
|
||||
175
examples/perfbench/perfbench.vcxproj
Normal file
175
examples/perfbench/perfbench.vcxproj
Normal 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>
|
||||
61
examples/perfbench/perfbench_serial.cpp
Normal file
61
examples/perfbench/perfbench_serial.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
1
expr.h
1
expr.h
@@ -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();
|
||||
|
||||
2
func.cpp
2
func.cpp
@@ -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());
|
||||
|
||||
1
ispc.cpp
1
ispc.cpp
@@ -497,6 +497,7 @@ Opt::Opt() {
|
||||
disableMaskedStoreToStore = false;
|
||||
disableGatherScatterFlattening = false;
|
||||
disableUniformMemoryOptimizations = false;
|
||||
disableCoalescing = false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
5
ispc.h
5
ispc.h
@@ -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.
|
||||
|
||||
38
llvmutil.cpp
38
llvmutil.cpp
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
13
main.cpp
13
main.cpp
@@ -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"))
|
||||
|
||||
71
module.cpp
71
module.cpp
@@ -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;
|
||||
|
||||
160
parse.yy
160
parse.yy
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
339
stdlib.ispc
339
stdlib.ispc
@@ -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();
|
||||
}
|
||||
|
||||
44
stmt.cpp
44
stmt.cpp
@@ -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
23
tests/acos.ispc
Normal 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
23
tests/asin.ispc
Normal 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; }
|
||||
@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
|
||||
void foo(float * uniform a) {
|
||||
void foo(varying float * uniform a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
14
tests/coalesce-1.ispc
Normal 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
14
tests/coalesce-2.ispc
Normal 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
14
tests/coalesce-3.ispc
Normal 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
17
tests/coalesce-4.ispc
Normal 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
20
tests/coalesce-5.ispc
Normal 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
21
tests/coalesce-6.ispc
Normal 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
21
tests/coalesce-7.ispc
Normal 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
19
tests/coalesce-8.ispc
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
17
tests/memcpy-uniform.ispc
Normal 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
21
tests/memcpy-varying.ispc
Normal 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;
|
||||
}
|
||||
16
tests/memmove-uniform.ispc
Normal file
16
tests/memmove-uniform.ispc
Normal 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);
|
||||
}
|
||||
19
tests/memmove-varying.ispc
Normal file
19
tests/memmove-varying.ispc
Normal 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
16
tests/memset-uniform.ispc
Normal 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
21
tests/memset-varying.ispc
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
|
||||
float foo(float * uniform a) {
|
||||
float foo(varying float * uniform a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
|
||||
float foo(float * uniform a) {
|
||||
float foo(varying float * uniform a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
|
||||
void foo(float * uniform a) {
|
||||
void foo(varying float * uniform a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
|
||||
void foo(float * uniform a) {
|
||||
void foo(varying float * uniform a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
|
||||
void foo(float * uniform a) {
|
||||
void foo(varying float * uniform a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
14
tests/ptr-varying-unif-index.ispc
Normal file
14
tests/ptr-varying-unif-index.ispc
Normal 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;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
|
||||
float foo(float * uniform a) {
|
||||
float foo(varying float * uniform a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
|
||||
30
tests/scatter-struct-with-array-member.ispc
Normal file
30
tests/scatter-struct-with-array-member.ispc
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
21
tests/soa-1.ispc
Normal 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
23
tests/soa-10.ispc
Normal 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
15
tests/soa-11.ispc
Normal 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
17
tests/soa-12.ispc
Normal 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
26
tests/soa-13.ispc
Normal 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
57
tests/soa-14.ispc
Normal 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
48
tests/soa-15.ispc
Normal 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
48
tests/soa-16.ispc
Normal 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
50
tests/soa-17.ispc
Normal 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
25
tests/soa-18.ispc
Normal 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
Reference in New Issue
Block a user