Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add named component access and swizzling operations to vector types. #836

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ TESTSUITE ( aastep allowconnect-err and-or-not-synonyms arithmetic
oslc-err-outputparamvararray oslc-err-paramdefault
oslc-err-struct-array-init oslc-err-struct-ctr
oslc-err-struct-dup oslc-err-struct-print
oslc-err-swizzle
oslc-warn-commainit
oslc-variadic-macro
oslc-version
Expand All @@ -296,6 +297,7 @@ TESTSUITE ( aastep allowconnect-err and-or-not-synonyms arithmetic
struct-isomorphic-overload struct-layers
struct-operator-overload struct-return struct-with-array
struct-nested struct-nested-assign struct-nested-deep
swizzle
ternary
testshade-expr
texture-alpha texture-blur texture-connected-options
Expand Down
156 changes: 146 additions & 10 deletions src/liboslcomp/ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,12 +694,12 @@ ASTindex::childname (size_t i) const

ASTstructselect::ASTstructselect (OSLCompilerImpl *comp, ASTNode *expr,
ustring field)
: ASTNode (structselect_node, comp, 0, expr), m_field(field),
: ASTfieldselect (structselect_node, comp, expr, field),
m_structid(-1), m_fieldid(-1), m_fieldsym(NULL)
{
m_fieldsym = find_fieldsym (m_structid, m_fieldid);
if (m_fieldsym) {
m_fieldname = m_fieldsym->name();
m_fullname = m_fieldsym->name();
m_typespec = m_fieldsym->typespec();
}
}
Expand All @@ -712,12 +712,7 @@ ASTstructselect::ASTstructselect (OSLCompilerImpl *comp, ASTNode *expr,
Symbol *
ASTstructselect::find_fieldsym (int &structid, int &fieldid)
{
if (! lvalue()->typespec().is_structure() &&
! lvalue()->typespec().is_structure_array()) {
error ("type '%s' does not have a member '%s'",
type_c_str(lvalue()->typespec()), m_field);
return NULL;
}
ASSERT (lvalue()->typespec().is_structure_based());

ustring structsymname;
TypeSpec structtype;
Expand Down Expand Up @@ -763,8 +758,7 @@ ASTstructselect::find_structsym (ASTNode *structnode, ustring &structname,
// or array of structs) down to a symbol that represents the
// particular field. In the process, we set structname and its
// type structtype.
ASSERT (structnode->typespec().is_structure() ||
structnode->typespec().is_structure_array());
ASSERT (structnode->typespec().is_structure_based());
if (structnode->nodetype() == variable_ref_node) {
// The structnode is a top-level struct variable
ASTvariable_ref *var = (ASTvariable_ref *) structnode;
Expand Down Expand Up @@ -811,6 +805,148 @@ ASTstructselect::print (std::ostream &out, int indentlevel) const



ASTswizzle::ASTswizzle (OSLCompilerImpl *comp, ASTNode *expr, ustring field) :
ASTfieldselect(swizzle_node, comp, expr, field)
{
if (field.size() == 1) m_typespec = TypeDesc::TypeFloat;
else m_typespec = TypeDesc::TypeVector;
}



size_t ASTswizzle::indices (ustring components, int *indexes, size_t N, bool consts)
{
size_t i = 0;
do {
switch (components[i]) {
case 'r':
case 'x': indexes[i] = 0; break;
case 'g':
case 'y': indexes[i] = 1; break;
case 'b':
case 'z': indexes[i] = 2; break;

case '0':
if (!consts)
return 0;
indexes[i] = const_offset;
break;
case '1':
if (!consts)
return 0;
indexes[i] = const_offset + 1;
break;

case '\0': return i;

default: return 0;
}
} while (++i < N);

return i;
}



const char *
ASTswizzle::childname (size_t i) const
{
return type_c_str(m_typespec);
}


void
ASTswizzle::print (std::ostream &out, int indentlevel) const
{
ASTNode::print (out, indentlevel);
indent (out, indentlevel+1);
out << "components " << field() << "\n";
}


ASTNode* ASTfieldselect::create (OSLCompilerImpl *comp, ASTNode *expr,
ustring field, bool swizzle)
{
if (!swizzle && expr->typespec().is_structure_based())
return new ASTstructselect (comp, expr, field);

const TypeSpec &type = expr->nodetype() != structselect_node ? expr->typespec() :
static_cast<ASTstructselect*>(expr)->fieldsym()->typespec();

if (type.aggregate() == TypeDesc::VEC3 && field.size() <= 3) {
// Early out swizle to native component ordering.
if (field == "rgb" || field == "xyz")
return expr;

ASTindex* index = expr->nodetype() != index_node ? nullptr : static_cast<ASTindex*>(expr);

int indexes[3];
switch (ASTswizzle::indices (field, indexes, 3, true)) {
case 1: {
// c.0 && c.1 not allowed
ASSERT (indexes[0] >= 0);
if (!index)
return new ASTindex (comp, expr, new ASTliteral (comp, indexes[0]));
index->extend(new ASTliteral (comp, indexes[0]));
return index;
}

case 3: {
bool allconst = true;
// Don't leak soon to be unused expr node
std::unique_ptr<ASTNode> cleanup(index);
ASTNode* index0 = nullptr;
if (index) {
index0 = index->index().get();
expr = index->lvalue().get();
}

ASTNode *args[3];
for (int i = 0; i < 3; ++i) {
if (indexes[i] >= 0) {
allconst = false;
args[i] = new ASTliteral (comp, indexes[i]);
if (i == 0 && index) {
// Re-use expr by extending the ASTindex.
index->extend (args[i]);
args[0] = cleanup.release ();
} else {
args[i] = !index0 ? new ASTindex (comp, expr, args[i]) :
new ASTindex (comp, expr, index0, args[i]);
}
} else {
float cval = indexes[i] - ASTswizzle::const_offset;
ASSERT ((cval==0) || (cval==1));
args[i] = new ASTliteral (comp, cval);
}
}
args[0]->append (args[1]);
args[1]->append (args[2]);

if (allconst) {
// return a type constructor instead of a swizzle
ASSERT (!cleanup);
// initial expression will be unused
cleanup.reset (expr);
return new ASTtype_constructor (comp, type, args[0]);
}
return new ASTswizzle (comp, args[0], field);
}

default: break;
}
}

comp->error (comp->filename(), comp->lineno(),
"type '%s' does not have a member '%s'",
comp->type_c_str(expr->typespec()), field);

// Don't leak expr node
delete expr;
return nullptr;
}


const char *
ASTconditional_statement::childname (size_t i) const
{
Expand Down
66 changes: 59 additions & 7 deletions src/liboslcomp/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ASTNode : public OIIO::RefCnt {
unknown_node, shader_declaration_node, function_declaration_node,
variable_declaration_node, compound_initializer_node,
variable_ref_node, preincdec_node, postincdec_node,
index_node, structselect_node,
index_node, structselect_node, swizzle_node,
conditional_statement_node,
loop_statement_node, loopmod_statement_node, return_statement_node,
binary_expression_node, unary_expression_node,
Expand Down Expand Up @@ -580,6 +580,13 @@ class ASTindex : public ASTNode
ustring destname, ustring srcname,
Symbol *index);

/// Add another index: obj[current][extended]
///
void extend (ASTNode *ext) {
ASSERT (nchildren() < 4);
m_children.emplace_back(ext);
}

ref lvalue () const { return child (0); }
ref index () const { return child (1); }
ref index2 () const { return child (2); }
Expand All @@ -588,7 +595,28 @@ class ASTindex : public ASTNode



class ASTstructselect : public ASTNode
class ASTfieldselect : public ASTNode
{
protected:
ASTfieldselect (NodeType type, OSLCompilerImpl *comp, ASTNode *expr,
ustring field) :
ASTNode(type, comp, Nothing, expr), m_field(field) {}

ustring m_field; ///< Name of the field
ustring m_fullname; ///< Full name of variable and field

public:
static ASTNode* create (OSLCompilerImpl *comp, ASTNode *expr, ustring field,
bool swizzle = false);

ustring field () const { return m_field; }
ustring fullname () const { return m_fullname; }
ref lvalue () const { return child (0); }
};



class ASTstructselect : public ASTfieldselect
{
public:
ASTstructselect (OSLCompilerImpl *comp, ASTNode *expr, ustring field);
Expand All @@ -602,9 +630,7 @@ class ASTstructselect : public ASTNode
/// field.
void codegen_assign (Symbol *dest, Symbol *src);

ref lvalue () const { return child (0); }
ustring field () const { return m_field; }
ustring fieldname () const { return m_fieldname; }
ustring fieldname () const { return fullname(); }
Symbol *fieldsym () const { return m_fieldsym; }

private:
Expand All @@ -613,15 +639,41 @@ class ASTstructselect : public ASTNode
TypeSpec &structtype);
Symbol *codegen_index ();

ustring m_field; ///< Name of the field
int m_structid; ///< index of the structure
int m_fieldid; ///< index of the field within the structure
ustring m_fieldname; ///< Name of the field variable
Symbol *m_fieldsym; ///< Symbol of the field variable
};



class ASTswizzle : public ASTfieldselect
{
public:
ASTswizzle (OSLCompilerImpl *comp, ASTNode *expr, ustring field);

const char *nodetypename () const { return "swizzle"; }
const char *childname (size_t i) const;
void print (std::ostream &out, int indentlevel=0) const;
TypeSpec typecheck (TypeSpec expected);
Symbol *codegen (Symbol *dest = NULL);

/// Get component indeces for a swizzle string.
/// consts allows '0' & '1' to be included in the string.
static size_t indices (ustring components, int *indexes, size_t N, bool consts);

/// Offset if the component index is really a constant ('0' or '1').
enum { const_offset = -2 };

/// Special code generation of assignment of src to proper components.
Symbol* codegen_assign (Symbol *src);

size_t indices (int *indexes, size_t N, bool consts) const {
return indices (m_field, indexes, N, consts);
}
};



class ASTconditional_statement : public ASTNode
{
public:
Expand Down
74 changes: 74 additions & 0 deletions src/liboslcomp/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,8 @@ ASTassign_expression::codegen (Symbol *dest)
// Assigning to an individual component or array element
index = (ASTindex *) var().get();
dest = NULL;
} else if (var()->nodetype() == swizzle_node) {
return static_cast<ASTswizzle*>(var().get())->codegen_assign(expr()->codegen (dest));
} else if (var()->nodetype() == structselect_node) {
dest = var()->codegen();
} else {
Expand Down Expand Up @@ -1294,6 +1296,78 @@ ASTindex::codegen_assign (Symbol *src, Symbol *ind,



Symbol *
ASTswizzle::codegen (Symbol *dest)
{
// General case, construct a new triple with the swizzle indexes.

if (dest == nullptr || ! equivalent (dest->typespec(), typespec()))
dest = m_compiler->make_temporary (typespec());

Symbol *syms[4] = { dest };

int nsym = 1;
for (ref arg = child(0); arg && nsym < 4; ++nsym, arg = arg->next()) {
syms[nsym] = arg->codegen();
}
ASSERT (nsym == 4);

// emit the constructor call
emitcode (typespec().string().c_str(), nsym, syms);
return dest;
}



Symbol *
ASTswizzle::codegen_assign (Symbol *src)
{
// Swizzle assignment.

if (!m_is_lvalue) {
error ("Cannot assign to constant swizzle");
return nullptr;
}

// First child is an index, which holds the lvalue (this) to store to.
ASTindex* index = static_cast<ASTindex*>(child(0));
Symbol *syms[4] = { index->lvalue().get()->codegen() };

int nsym = 0;
if (src->typespec().is_triple()) {
int idxs[3];
if (indices(idxs, 3, false /*can't assign to constants*/) != 3) {
error ("Trying to assign to invalid swizzle");
return nullptr;
}
// Iterate all of the indexes
for (; index && nsym < 3; ++nsym, index = static_cast<ASTindex*>(index->nextptr())) {
// tmp[I] = src[N]
ASSERT (index->nodetype() == index_node);
Symbol* sym = m_compiler->make_temporary (TypeDesc::TypeFloat);
emitcode ("compref", sym, src, m_compiler->make_constant (nsym));

// sym[0] is taken, so assign to syms[i+1]
ASSERT (idxs[nsym]+1 < int(sizeof(syms)/sizeof(syms[0])));
syms[idxs[nsym]+1] = sym;
}
} else {
// Same assignment to all components
for (int n = m_typespec.aggregate(); nsym < n; ++nsym)
syms[nsym] = src;
}

ASSERT (nsym == 3);

// emit the constructor call
emitcode (typespec().string().c_str(), nsym+1, syms);

// so transitive assignment will work
return syms[0];
}



Symbol *
ASTstructselect::codegen (Symbol *dest)
{
Expand Down
Loading