diff --git a/expr.cpp b/expr.cpp index 5c19347b..5eb0ce34 100644 --- a/expr.cpp +++ b/expr.cpp @@ -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 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(refTarget); + const VectorType* vectorType + = dynamic_cast(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(exprType); + if (structType != NULL) + return new StructMemberExpr(e, id, p, idpos, structType); + + const VectorType* vectorType = dynamic_cast(exprType); + if (vectorType != NULL) + return new VectorMemberExpr(e, id, p, idpos, vectorType); + + const ReferenceType* referenceType = dynamic_cast(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(exprType); - const VectorType *vectorType = dynamic_cast(exprType); - if (!structType && !vectorType) { - const ReferenceType *referenceType = - dynamic_cast(exprType); - const Type *refTarget = (referenceType == NULL) ? NULL : - referenceType->GetReferenceTarget(); - if ((structType = dynamic_cast(refTarget)) == NULL && - (vectorType = dynamic_cast(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(exprType); - const VectorType *vectorType = dynamic_cast(exprType); - if (!structType && !vectorType) { - const ReferenceType *referenceType = - dynamic_cast(exprType); - const Type *refTarget = (referenceType == NULL) ? NULL : - referenceType->GetReferenceTarget() ; - if ((structType = dynamic_cast(refTarget)) == NULL && - (vectorType = dynamic_cast(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(exprType); - const VectorType *vectorType = dynamic_cast(exprType); - llvm::Value *basePtr = NULL; - if (structType || vectorType) - basePtr = expr->GetLValue(ctx); - else { - const ReferenceType *referenceType = dynamic_cast(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(refTarget)) == NULL && - (vectorType = dynamic_cast(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; diff --git a/expr.h b/expr.h index f0d10b3c..92eb53bf 100644 --- a/expr.h +++ b/expr.h @@ -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; diff --git a/lex.ll b/lex.ll index b9eff107..e1e9ff33 100644 --- a/lex.ll +++ b/lex.ll @@ -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); } diff --git a/parse.yy b/parse.yy index 430ce92a..2ee40e7e 100644 --- a/parse.yy +++ b/parse.yy @@ -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 } */ diff --git a/tests/swizzle-1.ispc b/tests/swizzle-1.ispc new file mode 100644 index 00000000..5de70a56 --- /dev/null +++ b/tests/swizzle-1.ispc @@ -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; +} diff --git a/tests/swizzle-2.ispc b/tests/swizzle-2.ispc new file mode 100644 index 00000000..28a9e041 --- /dev/null +++ b/tests/swizzle-2.ispc @@ -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; +} diff --git a/tests/swizzle-3.ispc b/tests/swizzle-3.ispc new file mode 100644 index 00000000..2788e320 --- /dev/null +++ b/tests/swizzle-3.ispc @@ -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; +} diff --git a/tests/swizzle-4.ispc b/tests/swizzle-4.ispc new file mode 100644 index 00000000..d9bc762b --- /dev/null +++ b/tests/swizzle-4.ispc @@ -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; +} diff --git a/tests/swizzle-5.ispc b/tests/swizzle-5.ispc new file mode 100644 index 00000000..329f4a02 --- /dev/null +++ b/tests/swizzle-5.ispc @@ -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; +} diff --git a/tests/swizzle-6.ispc b/tests/swizzle-6.ispc new file mode 100644 index 00000000..1f016200 --- /dev/null +++ b/tests/swizzle-6.ispc @@ -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; +}