Add support for multi-element vector swizzles. Issue #17.

This commit adds support for swizzles like "foo.zy" (if "foo" is,
for example, a float<3> type) as rvalues.  (Still need support for
swizzles as lvalues.)
This commit is contained in:
Pete Couperus
2011-07-22 13:10:14 +01:00
committed by Matt Pharr
parent 98a2d69e72
commit 59036cdf5b
10 changed files with 397 additions and 140 deletions

418
expr.cpp
View File

@@ -2820,6 +2820,288 @@ IndexExpr::Print() const {
///////////////////////////////////////////////////////////////////////////
// MemberExpr
/** Map one character ids to vector element numbers. Allow a few different
conventions--xyzw, rgba, uv.
*/
static int
lIdentifierToVectorElement(char id) {
switch (id) {
case 'x':
case 'r':
case 'u':
return 0;
case 'y':
case 'g':
case 'v':
return 1;
case 'z':
case 'b':
return 2;
case 'w':
case 'a':
return 3;
default:
return -1;
}
}
class StructMemberExpr : public MemberExpr
{
public:
StructMemberExpr(Expr *e, const char *id, SourcePos p,
SourcePos idpos, const StructType* structType);
const Type* GetType() const;
int getElementNumber() const;
private:
const StructType* exprStructType;
};
StructMemberExpr::StructMemberExpr(Expr *e, const char *id, SourcePos p,
SourcePos idpos,
const StructType* structType)
: MemberExpr(e, id, p, idpos), exprStructType(structType) {
}
const Type*
StructMemberExpr::GetType() const {
// It's a struct, and the result type is the element
// type, possibly promoted to varying if the struct type / lvalue
// is varying.
const Type *elementType = exprStructType->GetElementType(identifier);
if (!elementType)
Error(identifierPos,
"Element name \"%s\" not present in struct type \"%s\".%s",
identifier.c_str(), exprStructType->GetString().c_str(),
getCandidateNearMatches().c_str());
if (exprStructType->IsVaryingType())
return elementType->GetAsVaryingType();
else
return elementType;
}
int
StructMemberExpr::getElementNumber() const {
int elementNumber = exprStructType->GetElementNumber(identifier);
if (elementNumber == -1)
Error(identifierPos,
"Element name \"%s\" not present in struct type \"%s\".%s",
identifier.c_str(), exprStructType->GetString().c_str(),
getCandidateNearMatches().c_str());
return elementNumber;
}
class VectorMemberExpr : public MemberExpr
{
public:
VectorMemberExpr(Expr *e, const char *id, SourcePos p,
SourcePos idpos, const VectorType* vectorType);
~VectorMemberExpr();
const Type* GetType() const;
llvm::Value* GetLValue(FunctionEmitContext* ctx) const;
llvm::Value* GetValue(FunctionEmitContext* ctx) const;
int getElementNumber() const;
private:
const VectorType* exprVectorType;
const VectorType* memberType;
};
VectorMemberExpr::VectorMemberExpr(Expr *e, const char *id, SourcePos p,
SourcePos idpos,
const VectorType* vectorType)
: MemberExpr(e, id, p, idpos), exprVectorType(vectorType) {
memberType = new VectorType(exprVectorType->GetElementType(),
identifier.length());
}
VectorMemberExpr::~VectorMemberExpr() {
delete memberType;
}
const Type*
VectorMemberExpr::GetType() const {
// For 1-element expressions, we have the base vector element
// type. For n-element expressions, we have a shortvec type
// with n > 1 elements. This can be changed when we get
// type<1> -> type conversions.
if (identifier.length() == 1) {
return exprVectorType->GetElementType();
} else {
return memberType;
}
}
llvm::Value*
VectorMemberExpr::GetLValue(FunctionEmitContext* ctx) const {
if (identifier.length() == 1) {
return MemberExpr::GetLValue(ctx);
} else {
return NULL;
}
}
llvm::Value*
VectorMemberExpr::GetValue(FunctionEmitContext* ctx) const {
if (identifier.length() == 1) {
return MemberExpr::GetValue(ctx);
} else {
std::vector<int> indices;
for (size_t i = 0; i < identifier.size(); ++i) {
int idx = lIdentifierToVectorElement(identifier[i]);
if (idx == -1)
Error(pos,
"Invalid swizzle charcter '%c' in swizzle \"%s\".",
identifier[i], identifier.c_str());
indices.push_back(idx);
}
llvm::Value *basePtr = expr->GetLValue(ctx);
if (basePtr == NULL) {
assert(m->errorCount > 0);
return NULL;
}
llvm::Value *ltmp = ctx->AllocaInst(memberType->LLVMType(g->ctx),
"vector_tmp");
ctx->SetDebugPos(pos);
for (size_t i = 0; i < identifier.size(); ++i) {
llvm::Value *ptmp =
ctx->GetElementPtrInst(ltmp, 0, i, "new_offset");
llvm::Value *initLValue =
ctx->GetElementPtrInst(basePtr , 0,
indices[i], "orig_offset");
llvm::Value *initValue =
ctx->LoadInst(initLValue, memberType->GetElementType(),
"vec_element");
ctx->StoreInst(initValue, ptmp);
}
return ctx->LoadInst(ltmp, memberType, "swizzle_vec");
}
}
int
VectorMemberExpr::getElementNumber() const {
int elementNumber = lIdentifierToVectorElement(identifier[0]);
if (elementNumber == -1)
Error(pos, "Vector element identifier \"%s\" unknown.",
identifier.c_str());
return elementNumber;
}
class ReferenceMemberExpr : public MemberExpr
{
public:
ReferenceMemberExpr(Expr *e, const char *id, SourcePos p,
SourcePos idpos, const ReferenceType* referenceType);
const Type* GetType() const;
int getElementNumber() const;
llvm::Value* GetLValue(FunctionEmitContext* ctx) const;
private:
const ReferenceType* exprReferenceType;
MemberExpr* dereferencedExpr;
};
ReferenceMemberExpr::ReferenceMemberExpr(Expr *e, const char *id, SourcePos p,
SourcePos idpos,
const ReferenceType* referenceType)
: MemberExpr(e, id, p, idpos), exprReferenceType(referenceType) {
const Type* refTarget = exprReferenceType->GetReferenceTarget();
const StructType* structType
= dynamic_cast<const StructType *>(refTarget);
const VectorType* vectorType
= dynamic_cast<const VectorType *>(refTarget);
if (structType != NULL) {
dereferencedExpr = new StructMemberExpr(e, id, p, idpos, structType);
} else if (vectorType != NULL) {
dereferencedExpr = new VectorMemberExpr(e, id, p, idpos, vectorType);
} else {
dereferencedExpr = NULL;
}
}
const Type*
ReferenceMemberExpr::GetType() const {
if (dereferencedExpr == NULL) {
Error(pos, "Can't access member of non-struct/vector type \"%s\".",
exprReferenceType->GetString().c_str());
return NULL;
} else {
return dereferencedExpr->GetType();
}
}
int
ReferenceMemberExpr::getElementNumber() const {
if (dereferencedExpr == NULL) {
// FIXME: I think we shouldn't ever get here and that
// typechecking should have caught this case
return -1;
} else {
return dereferencedExpr->getElementNumber();
}
}
llvm::Value*
ReferenceMemberExpr::GetLValue(FunctionEmitContext* ctx) const {
if (dereferencedExpr == NULL) {
// FIXME: again I think typechecking should have caught this
Error(pos, "Can't access member of non-struct/vector type \"%s\".",
exprReferenceType->GetString().c_str());
return NULL;
}
//FIXME: Minor Code-dup...this is the same as the base, except
// llvm::Value *basePtr = expr->GetLValue instead of expr->getValue
llvm::Value *basePtr = expr->GetValue(ctx);
if (!basePtr)
return NULL;
int elementNumber = getElementNumber();
if (elementNumber == -1)
return NULL;
ctx->SetDebugPos(pos);
return ctx->GetElementPtrInst(basePtr, 0, elementNumber);
}
MemberExpr*
MemberExpr::create(Expr *e, const char *id, SourcePos p, SourcePos idpos) {
const Type* exprType;
if (e == NULL || (exprType = e->GetType()) == NULL)
return new MemberExpr(e, id, p, idpos);
const StructType* structType = dynamic_cast<const StructType*>(exprType);
if (structType != NULL)
return new StructMemberExpr(e, id, p, idpos, structType);
const VectorType* vectorType = dynamic_cast<const VectorType*>(exprType);
if (vectorType != NULL)
return new VectorMemberExpr(e, id, p, idpos, vectorType);
const ReferenceType* referenceType = dynamic_cast<const ReferenceType*>(exprType);
if (referenceType != NULL)
return new ReferenceMemberExpr(e, id, p, idpos, referenceType);
return new MemberExpr(e, id, p, idpos);
}
MemberExpr::MemberExpr(Expr *e, const char *id, SourcePos p, SourcePos idpos)
: Expr(p), identifierPos(idpos) {
expr = e;
@@ -2861,48 +3143,7 @@ MemberExpr::GetValue(FunctionEmitContext *ctx) const {
const Type *
MemberExpr::GetType() const {
if (!expr)
return NULL;
const Type *exprType = expr->GetType();
if (!exprType)
return NULL;
const StructType *structType = dynamic_cast<const StructType *>(exprType);
const VectorType *vectorType = dynamic_cast<const VectorType *>(exprType);
if (!structType && !vectorType) {
const ReferenceType *referenceType =
dynamic_cast<const ReferenceType *>(exprType);
const Type *refTarget = (referenceType == NULL) ? NULL :
referenceType->GetReferenceTarget();
if ((structType = dynamic_cast<const StructType *>(refTarget)) == NULL &&
(vectorType = dynamic_cast<const VectorType *>(refTarget)) == NULL) {
Error(pos, "Can't access member of non-struct/vector type \"%s\".",
exprType->GetString().c_str());
return NULL;
}
}
if (vectorType != NULL)
// only one-element vector selection is supported for now (i.e. no
// swizzling "foo.xxy"), so the result type is always just the
// element type.
return vectorType->GetElementType();
else {
// Otherwise it's a struct, and the result type is the element
// type, possibly promoted to varying if the struct type / lvalue
// is varying.
const Type *elementType = structType->GetElementType(identifier);
if (!elementType)
Error(identifierPos, "Element name \"%s\" not present in struct type \"%s\".%s",
identifier.c_str(), structType->GetString().c_str(),
getCandidateNearMatches().c_str());
if (exprType->IsVaryingType())
return elementType->GetAsVaryingType();
else
return elementType;
}
return NULL;
}
@@ -2912,106 +3153,23 @@ MemberExpr::GetBaseSymbol() const {
}
/** Map one character ids to vector element numbers. Allow a few different
conventions--xyzw, rgba, uv.
*/
static int
lIdentifierToVectorElement(char id) {
switch (id) {
case 'x':
case 'r':
case 'u':
return 0;
case 'y':
case 'g':
case 'v':
return 1;
case 'z':
case 'b':
return 2;
case 'w':
case 'a':
return 3;
default:
return -1;
}
}
int
MemberExpr::getElementNumber() const {
const Type *exprType;
if (!expr || ((exprType = expr->GetType()) == NULL))
return -1;
const StructType *structType = dynamic_cast<const StructType *>(exprType);
const VectorType *vectorType = dynamic_cast<const VectorType *>(exprType);
if (!structType && !vectorType) {
const ReferenceType *referenceType =
dynamic_cast<const ReferenceType *>(exprType);
const Type *refTarget = (referenceType == NULL) ? NULL :
referenceType->GetReferenceTarget() ;
if ((structType = dynamic_cast<const StructType *>(refTarget)) == NULL &&
(vectorType = dynamic_cast<const VectorType *>(refTarget)) == NULL)
// FIXME: I think we shouldn't ever get here and that
// typechecking should have caught this case
return -1;
}
int elementNumber = -1;
if (vectorType) {
if (identifier.size() != 1) {
Error(pos, "Only single-character vector element accessors are currently "
"supported--\"%s\" is invalid. Sorry.", identifier.c_str());
}
else {
elementNumber = lIdentifierToVectorElement(identifier[0]);
if (elementNumber == -1)
Error(pos, "Vector element identifier \"%s\" unknown.",
identifier.c_str());
}
}
else {
elementNumber = structType->GetElementNumber(identifier);
if (elementNumber == -1)
Error(identifierPos, "Element name \"%s\" not present in struct type \"%s\".%s",
identifier.c_str(), structType->GetString().c_str(),
getCandidateNearMatches().c_str());
}
return elementNumber;
return -1;
}
llvm::Value *
MemberExpr::GetLValue(FunctionEmitContext *ctx) const {
//This kindof feels like magic, but this functionality
// will have to be overridden in VectorMemberExpr when
// we support multi-swizzle.
const Type *exprType;
if (!expr || ((exprType = expr->GetType()) == NULL))
return NULL;
ctx->SetDebugPos(pos);
const StructType *structType = dynamic_cast<const StructType *>(exprType);
const VectorType *vectorType = dynamic_cast<const VectorType *>(exprType);
llvm::Value *basePtr = NULL;
if (structType || vectorType)
basePtr = expr->GetLValue(ctx);
else {
const ReferenceType *referenceType = dynamic_cast<const ReferenceType *>(exprType);
// FIXME: store structType and vectorType as members, or do all
// this in a separate function? This code to figure out
// struct/vectorType is replicated a bunch of times in
// MemberExpr...
const Type *refTarget = (referenceType == NULL) ? NULL :
referenceType->GetReferenceTarget() ;
if ((structType = dynamic_cast<const StructType *>(refTarget)) == NULL &&
(vectorType = dynamic_cast<const VectorType *>(refTarget)) == NULL) {
// FIXME: again I think typechecking should have caught this
Error(pos, "Can't access member of non-struct/vector type \"%s\".",
exprType->GetString().c_str());
return NULL;
}
basePtr = expr->GetValue(ctx);
}
llvm::Value *basePtr = expr->GetLValue(ctx);
if (!basePtr)
return NULL;

23
expr.h
View File

@@ -292,23 +292,28 @@ private:
/** @brief Expression representing member selection ("foo.bar").
*
* This will also be overloaded to deal with swizzles.
*/
class MemberExpr : public Expr {
public:
static MemberExpr* create(Expr *expr, const char *identifier,
SourcePos pos, SourcePos identifierPos);
MemberExpr(Expr *expr, const char *identifier, SourcePos pos,
SourcePos identifierPos);
llvm::Value *GetValue(FunctionEmitContext *ctx) const;
llvm::Value *GetLValue(FunctionEmitContext *ctx) const;
const Type *GetType() const;
Symbol *GetBaseSymbol() const;
void Print() const;
Expr *Optimize();
Expr *TypeCheck();
virtual llvm::Value *GetValue(FunctionEmitContext *ctx) const;
virtual llvm::Value *GetLValue(FunctionEmitContext *ctx) const;
virtual const Type *GetType() const;
virtual Symbol *GetBaseSymbol() const;
virtual void Print() const;
virtual Expr *Optimize();
virtual Expr *TypeCheck();
virtual int getElementNumber() const;
private:
protected:
std::string getCandidateNearMatches() const;
int getElementNumber() const;
Expr *expr;
std::string identifier;

1
lex.ll
View File

@@ -72,6 +72,7 @@ FLOAT_NUMBER (([0-9]+|(([0-9]+\.[0-9]*[fF]?)|(\.[0-9]+)))([eE][-+]?[0-9]+)?[fF]?
HEX_FLOAT_NUMBER (0x[01](\.[0-9a-fA-F]*)?p[-+]?[0-9]+[fF]?)
IDENT [a-zA-Z_][a-zA-Z_0-9]*
ZO_SWIZZLE ([01]+[w-z]+)+|([01]+[rgba]+)+|([01]+[uv]+)+
%%
"/*" { lCComment(yylloc); }

View File

@@ -269,7 +269,7 @@ postfix_expression
| TOKEN_LAUNCH '<' postfix_expression '(' ')' '>'
{ $$ = new FunctionCallExpr($3, new ExprList(@3), @3, true); }
| postfix_expression '.' TOKEN_IDENTIFIER
{ $$ = new MemberExpr($1, yytext, @1, @3); }
{ $$ = MemberExpr::create($1, yytext, @1, @3); }
/* | postfix_expression TOKEN_PTR_OP TOKEN_IDENTIFIER
{ UNIMPLEMENTED }
*/

15
tests/swizzle-1.ispc Normal file
View File

@@ -0,0 +1,15 @@
export uniform int width() { return programCount; }
export void f_v(uniform float RET[]) {
float<3> a = {1,2,3};
float<3> b = a.zxy;
RET[programIndex] = b.x;
}
export void result(uniform float RET[]) {
RET[programIndex] = 3;
}

15
tests/swizzle-2.ispc Normal file
View File

@@ -0,0 +1,15 @@
export uniform int width() { return programCount; }
export void f_v(uniform float RET[]) {
float<3> a = {1,10,100};
float<3> b = a.zxy;
RET[programIndex] = b.x + 2*b.y + 3*b.z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 132;
}

15
tests/swizzle-3.ispc Normal file
View File

@@ -0,0 +1,15 @@
export uniform int width() { return programCount; }
export void f_v(uniform float RET[]) {
float<3> a = {1,10,100};
float<6> b = a.zxyyxz;
RET[programIndex] = b.x + 2*b.y + 3*b.z - 3*b[3] - 2*b[4] - b[5];
}
export void result(uniform float RET[]) {
RET[programIndex] = 0;
}

15
tests/swizzle-4.ispc Normal file
View File

@@ -0,0 +1,15 @@
export uniform int width() { return programCount; }
export void f_v(uniform float RET[]) {
float<3> a = {1,2,3};
float<2> b = a.xy - a.uv;
RET[programIndex] = b.x + 2*b.y;
}
export void result(uniform float RET[]) {
RET[programIndex] = 0;
}

15
tests/swizzle-5.ispc Normal file
View File

@@ -0,0 +1,15 @@
export uniform int width() { return programCount; }
export void f_v(uniform float RET[]) {
float<3> a = {1,2,3};
float<3> b = a.rgb;
RET[programIndex] = b.x + 2*b.y + 3*b.z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 14;
}

18
tests/swizzle-6.ispc Normal file
View File

@@ -0,0 +1,18 @@
export uniform int width() { return programCount; }
export void f_v(uniform float RET[]) {
float<3> a = {1,2,3};
float<3> b1 = a.yzx;
float<3> b2 = b1.yzx;
float<3> b3 = b2.yzx;
float<3> b = b3 - a;
RET[programIndex] = b.x + 2*b.y + 3*b.z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 0;
}