TypeSpec ASTassign_expression::typecheck (TypeSpec expected) { TypeSpec vt = var()->typecheck (); TypeSpec et = expr()->typecheck (vt); if (! var()->is_lvalue()) { error ("Can't assign via %s to something that isn't an lvalue", opname()); return TypeSpec(); } ASSERT (m_op == Assign); // all else handled by binary_op // We don't currently support assignment of whole arrays if (vt.is_array() || et.is_array()) { error ("Can't assign entire arrays"); return TypeSpec(); } // Special case: ok to assign a literal 0 to a closure to // initialize it. if (vt.is_closure() && ! et.is_closure() && (et.is_float() || et.is_int()) && expr()->nodetype() == literal_node && ((ASTliteral *)&(*expr()))->floatval() == 0.0f) { return TypeSpec(); // it's ok } // If either argument is a structure, they better both be the same // exact kind of structure. if (vt.is_structure() || et.is_structure()) { int vts = vt.structure(), ets = et.structure(); if (vts == ets) return m_typespec = vt; // Otherwise, a structure mismatch error ("Cannot assign '%s' to '%s'", type_c_str(et), type_c_str(vt)); return TypeSpec(); } // Expression must be of a type assignable to the lvalue if (! assignable (vt, et)) { error ("Cannot assign '%s' to '%s'", type_c_str(et), type_c_str(vt)); // FIXME - can we print the variable in question? return TypeSpec(); } return m_typespec = vt; }
TypeSpec ASTternary_expression::typecheck (TypeSpec expected) { // FIXME - closures TypeSpec c = typecheck_list (cond(), TypeDesc::TypeInt); TypeSpec t = typecheck_list (trueexpr(), expected); TypeSpec f = typecheck_list (falseexpr(), expected); if (c.is_closure()) error ("Cannot use a closure as a condition"); if (c.is_structure()) error ("Cannot use a struct as a condition"); if (c.is_array()) error ("Cannot use an array as a condition"); // No arrays if (t.is_array() || t.is_array()) { error ("Not allowed: '%s ? %s : %s'", type_c_str(c), type_c_str(t), type_c_str(f)); return TypeSpec (); } // The true and false clauses need to be equivalent types, or one // needs to be assignable to the other (so one can be upcast). if (assignable (t, f) || assignable (f, t)) m_typespec = higherprecision (t.simpletype(), f.simpletype()); else error ("Not allowed: '%s ? %s : %s'", type_c_str(c), type_c_str(t), type_c_str(f)); return m_typespec; }
TypeSpec ASTunary_expression::typecheck (TypeSpec expected) { // FIXME - closures typecheck_children (expected); TypeSpec t = expr()->typespec(); if (t.is_structure()) { error ("Can't do '%s' to a %s.", opname(), type_c_str(t)); return TypeSpec (); } switch (m_op) { case Sub : case Add : if (t.is_string()) { error ("Can't do '%s' to a %s.", opname(), type_c_str(t)); return TypeSpec (); } m_typespec = t; break; case Not : m_typespec = TypeDesc::TypeInt; // ! is always an int break; case Compl : if (! t.is_int()) { error ("Operator '~' can only be done to an int"); return TypeSpec (); } m_typespec = t; break; default: error ("unknown unary operator"); } return m_typespec; }
TypeSpec ASTindex::typecheck (TypeSpec expected) { typecheck_children (); const char *indextype = ""; TypeSpec t = lvalue()->typespec(); if (t.is_structure()) { error ("Cannot use [] indexing on a struct"); return TypeSpec(); } if (t.is_closure()) { error ("Cannot use [] indexing on a closure"); return TypeSpec(); } if (index3()) { if (! t.is_array() && ! t.elementtype().is_matrix()) error ("[][][] only valid for a matrix array"); m_typespec = TypeDesc::FLOAT; } else if (t.is_array()) { indextype = "array"; m_typespec = t.elementtype(); if (index2()) { if (t.aggregate() == TypeDesc::SCALAR) error ("can't use [][] on a simple array"); m_typespec = TypeDesc::FLOAT; } } else if (t.aggregate() == TypeDesc::VEC3) { indextype = "component"; TypeDesc tnew = t.simpletype(); tnew.aggregate = TypeDesc::SCALAR; tnew.vecsemantics = TypeDesc::NOXFORM; m_typespec = tnew; if (index2()) error ("can't use [][] on a %s", type_c_str(t)); } else if (t.aggregate() == TypeDesc::MATRIX44) { indextype = "component"; TypeDesc tnew = t.simpletype(); tnew.aggregate = TypeDesc::SCALAR; tnew.vecsemantics = TypeDesc::NOXFORM; m_typespec = tnew; if (! index2()) error ("must use [][] on a matrix, not just []"); } else { error ("can only use [] indexing for arrays or multi-component types"); return TypeSpec(); } // Make sure the indices (children 1+) are integers for (size_t c = 1; c < nchildren(); ++c) if (! child(c)->typespec().is_int()) error ("%s index must be an integer, not a %s", indextype, type_c_str(index()->typespec())); // If the thing we're indexing is an lvalue, so is the indexed element m_is_lvalue = lvalue()->is_lvalue(); return m_typespec; }
void OSOReaderToMaster::symbol (SymType symtype, TypeSpec typespec, const char *name_) { ustring name(name_); Symbol sym (name, typespec, symtype); TypeDesc t = typespec.simpletype(); int nvals = t.aggregate * (t.is_unsized_array() ? 1 : t.numelements()); if (sym.symtype() == SymTypeParam || sym.symtype() == SymTypeOutputParam) { // Skip structs for now, they're just placeholders if (typespec.is_structure()) { } else if (typespec.simpletype().basetype == TypeDesc::FLOAT) { sym.dataoffset ((int) m_master->m_fdefaults.size()); expand (m_master->m_fdefaults, nvals); } else if (typespec.simpletype().basetype == TypeDesc::INT) { sym.dataoffset ((int) m_master->m_idefaults.size()); expand (m_master->m_idefaults, nvals); } else if (typespec.simpletype().basetype == TypeDesc::STRING) { sym.dataoffset ((int) m_master->m_sdefaults.size()); expand (m_master->m_sdefaults, nvals); } else if (typespec.is_closure_based()) { // Closures are pointers, so we allocate a string default taking // adventage of their default being NULL as well. sym.dataoffset ((int) m_master->m_sdefaults.size()); expand (m_master->m_sdefaults, nvals); } else { ASSERT (0 && "unexpected type"); } } if (sym.symtype() == SymTypeConst) { if (typespec.simpletype().basetype == TypeDesc::FLOAT) { sym.dataoffset ((int) m_master->m_fconsts.size()); expand (m_master->m_fconsts, nvals); } else if (typespec.simpletype().basetype == TypeDesc::INT) { sym.dataoffset ((int) m_master->m_iconsts.size()); expand (m_master->m_iconsts, nvals); } else if (typespec.simpletype().basetype == TypeDesc::STRING) { sym.dataoffset ((int) m_master->m_sconsts.size()); expand (m_master->m_sconsts, nvals); } else { ASSERT (0 && "unexpected type"); } } #if 0 // FIXME -- global_heap_offset is quite broken. But also not necessary. // We made need to fix this later. if (sym.symtype() == SymTypeGlobal) { sym.dataoffset (m_shadingsys.global_heap_offset (sym.name())); } #endif sym.lockgeom (m_shadingsys.lockgeom_default()); m_master->m_symbols.push_back (sym); m_symmap[name] = int(m_master->m_symbols.size()) - 1; // Start the index at which we add specified defaults m_sym_default_index = 0; }
llvm::Type * BackendLLVM::llvm_type_groupdata () { // If already computed, return it if (m_llvm_type_groupdata) return m_llvm_type_groupdata; std::vector<llvm::Type*> fields; // First, add the array that tells if each layer has run. But only make // slots for the layers that may be called/used. int sz = (m_num_used_layers + 3) & (~3); // Round up to 32 bit boundary fields.push_back (ll.type_array (ll.type_bool(), sz)); size_t offset = sz * sizeof(bool); // For each layer in the group, add entries for all params that are // connected or interpolated, and output params. Also mark those // symbols with their offset within the group struct. if (llvm_debug() >= 2) std::cout << "Group param struct:\n"; m_param_order_map.clear (); int order = 1; for (int layer = 0; layer < group().nlayers(); ++layer) { ShaderInstance *inst = group()[layer]; if (inst->unused()) continue; FOREACH_PARAM (Symbol &sym, inst) { TypeSpec ts = sym.typespec(); if (ts.is_structure()) // skip the struct symbol itself continue; int arraylen = std::max (1, sym.typespec().arraylength()); int deriv_mult = sym.has_derivs() ? 3 : 1; int n = arraylen * deriv_mult; ts.make_array (n); fields.push_back (llvm_type (ts)); // Alignment size_t align = sym.typespec().is_closure_based() ? sizeof(void*) : sym.typespec().simpletype().basesize(); if (offset & (align-1)) offset += align - (offset & (align-1)); if (llvm_debug() >= 2) std::cout << " " << inst->layername() << " (" << inst->id() << ") " << sym.mangled() << " " << ts.c_str() << ", field " << order << ", offset " << offset << std::endl; sym.dataoffset ((int)offset); offset += int(sym.size()) * deriv_mult; m_param_order_map[&sym] = order; ++order; } }
ASTvariable_declaration::ASTvariable_declaration (OSLCompilerImpl *comp, const TypeSpec &type, ustring name, ASTNode *init, bool isparam, bool ismeta, bool isoutput, bool initlist) : ASTNode (variable_declaration_node, comp, 0, init, NULL /* meta */), m_name(name), m_sym(NULL), m_isparam(isparam), m_isoutput(isoutput), m_ismetadata(ismeta), m_initlist(initlist) { m_typespec = type; Symbol *f = comp->symtab().clash (name); if (f && ! m_ismetadata) { std::string e = Strutil::format ("\"%s\" already declared in this scope", name.c_str()); if (f->node()) { std::string filename = OIIO::Filesystem::filename(f->node()->sourcefile().string()); e += Strutil::format ("\n\t\tprevious declaration was at %s:%d", filename, f->node()->sourceline()); } if (f->scope() == 0 && f->symtype() == SymTypeFunction && isparam) { // special case: only a warning for param to mask global function warning ("%s", e.c_str()); } else { error ("%s", e.c_str()); } } if (name[0] == '_' && name[1] == '_' && name[2] == '_') { error ("\"%s\" : sorry, can't start with three underscores", name.c_str()); } SymType symtype = isparam ? (isoutput ? SymTypeOutputParam : SymTypeParam) : SymTypeLocal; // Sneaky debugging aid: a local that starts with "__debug_tmp__" // gets declared as a temp. Don't do this on purpose!!! if (symtype == SymTypeLocal && Strutil::starts_with (name, "__debug_tmp__")) symtype = SymTypeTemp; m_sym = new Symbol (name, type, symtype, this); if (! m_ismetadata) oslcompiler->symtab().insert (m_sym); // A struct really makes several subvariables if (type.is_structure() || type.is_structure_array()) { ASSERT (! m_ismetadata); // Add the fields as individual declarations m_compiler->add_struct_fields (type.structspec(), m_sym->name(), symtype, type.is_unsized_array() ? -1 : type.arraylength(), this, init); } }
void OSOReaderQuery::symbol (SymType symtype, TypeSpec typespec, const char *name) { if (symtype == SymTypeParam || symtype == SymTypeOutputParam) { m_reading_param = true; OSLQuery::Parameter p; p.name = name; p.type = typespec.simpletype(); // FIXME -- struct & closure p.isoutput = (symtype == SymTypeOutputParam); p.varlenarray = (typespec.arraylength() < 0); p.isstruct = typespec.is_structure(); p.isclosure = typespec.is_closure(); m_query.m_params.push_back (p); } else { m_reading_param = false; } }
TypeSpec ASTconditional_statement::typecheck (TypeSpec expected) { typecheck_list (cond ()); oslcompiler->push_nesting (false); typecheck_list (truestmt ()); typecheck_list (falsestmt ()); oslcompiler->pop_nesting (false); TypeSpec c = cond()->typespec(); if (c.is_closure()) error ("Cannot use a closure as an 'if' condition"); if (c.is_structure()) error ("Cannot use a struct as an 'if' condition"); if (c.is_array()) error ("Cannot use an array as an 'if' condition"); return m_typespec = TypeDesc (TypeDesc::NONE); }
TypeSpec ASTloop_statement::typecheck (TypeSpec expected) { typecheck_list (init ()); oslcompiler->push_nesting (true); typecheck_list (cond ()); typecheck_list (iter ()); typecheck_list (stmt ()); oslcompiler->pop_nesting (true); TypeSpec c = cond()->typespec(); if (c.is_closure()) error ("Cannot use a closure as an '%s' condition", opname()); if (c.is_structure()) error ("Cannot use a struct as an '%s' condition", opname()); if (c.is_array()) error ("Cannot use an array as an '%s' condition", opname()); return m_typespec = TypeDesc (TypeDesc::NONE); }
std::string OSLCompilerImpl::code_from_type (TypeSpec type) const { std::string out; TypeDesc elem = type.elementtype().simpletype(); if (type.is_structure()) { out = Strutil::format ("S%d", type.structure()); } else if (type.is_closure()) { out = 'C'; } else { if (elem == TypeDesc::TypeInt) out = 'i'; else if (elem == TypeDesc::TypeFloat) out = 'f'; else if (elem == TypeDesc::TypeColor) out = 'c'; else if (elem == TypeDesc::TypePoint) out = 'p'; else if (elem == TypeDesc::TypeVector) out = 'v'; else if (elem == TypeDesc::TypeNormal) out = 'n'; else if (elem == TypeDesc::TypeMatrix) out = 'm'; else if (elem == TypeDesc::TypeString) out = 's'; else if (elem == TypeDesc::NONE) out = 'x'; else ASSERT (0); } if (type.is_array()) { int len = type.arraylength (); if (len > 0) out += Strutil::format ("[%d]", len); else out += "[]"; } return out; }
TypeSpec ASTbinary_expression::typecheck (TypeSpec expected) { typecheck_children (expected); TypeSpec l = left()->typespec(); TypeSpec r = right()->typespec(); // No binary ops work on structs or arrays if (l.is_structure() || r.is_structure() || l.is_array() || r.is_array()) { error ("Not allowed: '%s %s %s'", type_c_str(l), opname(), type_c_str(r)); return TypeSpec (); } // Special for closures -- just a few cases to worry about if (l.is_color_closure() || r.is_color_closure()) { if (m_op == Add) { if (l.is_color_closure() && r.is_color_closure()) return m_typespec = l; } if (m_op == Mul) { if (l.is_color_closure() && (r.is_color() || r.is_int_or_float())) return m_typespec = l; if (r.is_color_closure() && (l.is_color() || l.is_int_or_float())) { // N.B. Reorder so that it's always r = closure * k, // not r = k * closure. See codegen for why this helps. std::swap (m_children[0], m_children[1]); return m_typespec = r; } } // If we got this far, it's an op that's not allowed error ("Not allowed: '%s %s %s'", type_c_str(l), opname(), type_c_str(r)); return TypeSpec (); } switch (m_op) { case Sub : case Add : case Mul : case Div : // Add/Sub/Mul/Div work for any equivalent types, and // combination of int/float and other numeric types, but do not // work with strings. Add/Sub don't work with matrices, but // Mul/Div do. // FIXME -- currently, equivalent types combine to make the // left type. But maybe we should be more careful, for example // point-point -> vector, etc. if (l.is_string() || r.is_string()) break; // Dispense with strings trivially if ((m_op == Sub || m_op == Add) && (l.is_matrix() || r.is_matrix())) break; // Matrices don't combine for + and - if (equivalent (l, r) || (l.is_numeric() && r.is_int_or_float()) || (l.is_int_or_float() && r.is_numeric())) return m_typespec = higherprecision (l.simpletype(), r.simpletype()); break; case Mod : // Mod only works with ints, and return ints. if (l.is_int() && r.is_int()) return m_typespec = TypeDesc::TypeInt; break; case Equal : case NotEqual : // Any equivalent types can be compared with == and !=, also a // float or int can be compared to any other numeric type. // Result is always an int. if (equivalent (l, r) || (l.is_numeric() && r.is_int_or_float()) || (l.is_int_or_float() && r.is_numeric())) return m_typespec = TypeDesc::TypeInt; break; case Greater : case Less : case GreaterEqual : case LessEqual : // G/L comparisons only work with floats or ints, and always // return int. if (l.is_int_or_float() && r.is_int_or_float()) return m_typespec = TypeDesc::TypeInt; break; case BitAnd : case BitOr : case Xor : case ShiftLeft : case ShiftRight : // Bitwise ops only work with ints, and return ints. if (l.is_int() && r.is_int()) return m_typespec = TypeDesc::TypeInt; break; case And : case Or : // Logical ops work on any simple type (since they test for // nonzeroness), but always return int. return m_typespec = TypeDesc::TypeInt; default: error ("unknown binary operator"); } // If we got this far, it's an op that's not allowed error ("Not allowed: '%s %s %s'", type_c_str(l), opname(), type_c_str(r)); return TypeSpec (); }
llvm::Type * BackendLLVM::llvm_type_groupdata () { // If already computed, return it if (m_llvm_type_groupdata) return m_llvm_type_groupdata; std::vector<llvm::Type*> fields; int offset = 0; int order = 0; if (llvm_debug() >= 2) std::cout << "Group param struct:\n"; // First, add the array that tells if each layer has run. But only make // slots for the layers that may be called/used. if (llvm_debug() >= 2) std::cout << " layers run flags: " << m_num_used_layers << " at offset " << offset << "\n"; int sz = (m_num_used_layers + 3) & (~3); // Round up to 32 bit boundary fields.push_back (ll.type_array (ll.type_bool(), sz)); offset += sz * sizeof(bool); ++order; // Now add the array that tells which userdata have been initialized, // and the space for the userdata values. int nuserdata = (int) group().m_userdata_names.size(); if (nuserdata) { if (llvm_debug() >= 2) std::cout << " userdata initialized flags: " << nuserdata << " at offset " << offset << ", field " << order << "\n"; ustring *names = & group().m_userdata_names[0]; TypeDesc *types = & group().m_userdata_types[0]; int *offsets = & group().m_userdata_offsets[0]; int sz = (nuserdata + 3) & (~3); fields.push_back (ll.type_array (ll.type_bool(), sz)); offset += nuserdata * sizeof(bool); ++order; for (int i = 0; i < nuserdata; ++i) { TypeDesc type = types[i]; int n = type.numelements() * 3; // always make deriv room type.arraylen = n; fields.push_back (llvm_type (type)); // Alignment int align = type.basesize(); offset = OIIO::round_to_multiple_of_pow2 (offset, align); if (llvm_debug() >= 2) std::cout << " userdata " << names[i] << ' ' << type << ", field " << order << ", offset " << offset << "\n"; offsets[i] = offset; offset += int(type.size()); ++order; } } // For each layer in the group, add entries for all params that are // connected or interpolated, and output params. Also mark those // symbols with their offset within the group struct. m_param_order_map.clear (); for (int layer = 0; layer < group().nlayers(); ++layer) { ShaderInstance *inst = group()[layer]; if (inst->unused()) continue; FOREACH_PARAM (Symbol &sym, inst) { TypeSpec ts = sym.typespec(); if (ts.is_structure()) // skip the struct symbol itself continue; const int arraylen = std::max (1, sym.typespec().arraylength()); const int derivSize = (sym.has_derivs() ? 3 : 1); ts.make_array (arraylen * derivSize); fields.push_back (llvm_type (ts)); // Alignment size_t align = sym.typespec().is_closure_based() ? sizeof(void*) : sym.typespec().simpletype().basesize(); if (offset & (align-1)) offset += align - (offset & (align-1)); if (llvm_debug() >= 2) std::cout << " " << inst->layername() << " (" << inst->id() << ") " << sym.mangled() << " " << ts.c_str() << ", field " << order << ", size " << derivSize * int(sym.size()) << ", offset " << offset << std::endl; sym.dataoffset ((int)offset); offset += derivSize* int(sym.size()); m_param_order_map[&sym] = order; ++order; } }