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; }
bool OSLCompilerImpl::compile (const std::string &filename, const std::vector<std::string> &options) { if (! boost::filesystem::exists (filename)) { error (ustring(), 0, "Input file \"%s\" not found", filename.c_str()); return false; } std::string stdinclude; #ifdef USE_BOOST_WAVE std::vector<std::string> defines; std::vector<std::string> undefines; std::vector<std::string> includepaths; #else std::string cppoptions; #endif // Determine where the installed shader include directory is, and // look for ../shaders/stdosl.h and force it to include. std::string program = Sysutil::this_program_path (); if (program.size()) { boost::filesystem::path path (program); // our program #if BOOST_VERSION >= 103600 path = path.parent_path (); // now the bin dir of our program path = path.parent_path (); // now the parent dir #else path = path.branch_path (); // now the bin dir of our program path = path.branch_path (); // now the parent dir #endif path = path / "shaders"; bool found = false; if (boost::filesystem::exists (path)) { path = path / "stdosl.h"; if (boost::filesystem::exists (path)) { stdinclude = path.string(); found = true; } } if (! found) warning (ustring(filename), 0, "Unable to find \"%s\"", path.string().c_str()); } m_output_filename.clear (); bool preprocess_only = false; for (size_t i = 0; i < options.size(); ++i) { if (options[i] == "-v") { // verbose mode m_verbose = true; } else if (options[i] == "-q") { // quiet mode m_quiet = true; } else if (options[i] == "-d") { // debug mode m_debug = true; } else if (options[i] == "-E") { preprocess_only = true; } else if (options[i] == "-o" && i < options.size()-1) { ++i; m_output_filename = options[i]; } else if (options[i] == "-O0") { m_optimizelevel = 0; } else if (options[i] == "-O" || options[i] == "-O1") { m_optimizelevel = 1; } else if (options[i] == "-O2") { m_optimizelevel = 2; #ifdef USE_BOOST_WAVE } else if (options[i].c_str()[0] == '-' && options[i].size() > 2) { // options meant for the preprocessor if(options[i].c_str()[1] == 'D') defines.push_back(options[i].substr(2)); else if(options[i].c_str()[1] == 'U') undefines.push_back(options[i].substr(2)); else if(options[i].c_str()[1] == 'I') includepaths.push_back(options[i].substr(2)); #else } else { // something meant for the cpp command cppoptions += "\""; cppoptions += options[i]; cppoptions += "\" "; #endif } } std::string preprocess_result; #ifdef USE_BOOST_WAVE if (! preprocess(filename, stdinclude, defines, undefines, includepaths, preprocess_result)) { #else if (! preprocess(filename, stdinclude, cppoptions, preprocess_result)) { #endif return false; } else if (preprocess_only) { std::cout << preprocess_result; } else { std::istringstream in (preprocess_result); oslcompiler = this; // Create a lexer, parse the file, delete the lexer m_lexer = new oslFlexLexer (&in); oslparse (); bool parseerr = error_encountered(); delete m_lexer; if (! parseerr) { shader()->typecheck (); } // Print the parse tree if there were no errors if (m_debug) { symtab().print (); if (shader()) shader()->print (std::cout); } if (! error_encountered()) { shader()->codegen (); // add_useparam (); track_variable_dependencies (); track_variable_lifetimes (); check_for_illegal_writes (); // if (m_optimizelevel >= 1) // coalesce_temporaries (); } if (! error_encountered()) { if (m_output_filename.size() == 0) m_output_filename = default_output_filename (); write_oso_file (m_output_filename); } oslcompiler = NULL; } return ! error_encountered(); } struct GlobalTable { const char *name; TypeSpec type; }; static GlobalTable globals[] = { { "P", TypeDesc::TypePoint }, { "I", TypeDesc::TypeVector }, { "N", TypeDesc::TypeNormal }, { "Ng", TypeDesc::TypeNormal }, { "u", TypeDesc::TypeFloat }, { "v", TypeDesc::TypeFloat }, { "dPdu", TypeDesc::TypeVector }, { "dPdv", TypeDesc::TypeVector }, #if 0 // Light variables -- we don't seem to be on a route to support this // kind of light shader, so comment these out for now. { "L", TypeDesc::TypeVector }, { "Cl", TypeDesc::TypeColor }, { "Ns", TypeDesc::TypeNormal }, { "Pl", TypeDesc::TypePoint }, { "Nl", TypeDesc::TypeNormal }, #endif { "Ps", TypeDesc::TypePoint }, { "Ci", TypeSpec (TypeDesc::TypeColor, true) }, { "time", TypeDesc::TypeFloat }, { "dtime", TypeDesc::TypeFloat }, { "dPdtime", TypeDesc::TypeVector }, { NULL } }; void OSLCompilerImpl::initialize_globals () { for (int i = 0; globals[i].name; ++i) { Symbol *s = new Symbol (ustring(globals[i].name), globals[i].type, SymTypeGlobal); symtab().insert (s); } } std::string OSLCompilerImpl::default_output_filename () { if (m_shader && shader_decl()) return shader_decl()->shadername().string() + ".oso"; return std::string(); } void OSLCompilerImpl::write_oso_metadata (const ASTNode *metanode) const { ASSERT (metanode->nodetype() == ASTNode::variable_declaration_node); const ASTvariable_declaration *metavar = static_cast<const ASTvariable_declaration *>(metanode); Symbol *metasym = metavar->sym(); ASSERT (metasym); TypeSpec ts = metasym->typespec(); oso ("%%meta{%s,%s,", ts.string().c_str(), metasym->name().c_str()); const ASTNode *init = metavar->init().get(); ASSERT (init); if (ts.is_string() && init->nodetype() == ASTNode::literal_node) oso ("\"%s\"", ((const ASTliteral *)init)->strval()); else if (ts.is_int() && init->nodetype() == ASTNode::literal_node) oso ("%d", ((const ASTliteral *)init)->intval()); else if (ts.is_float() && init->nodetype() == ASTNode::literal_node) oso ("%.8g", ((const ASTliteral *)init)->floatval()); // FIXME -- what about type constructors? else { std::cout << "Error, don't know how to print metadata " << ts.string() << " with node type " << init->nodetypename() << "\n"; ASSERT (0); // FIXME } oso ("} "); }
void BackendLLVM::llvm_assign_initial_value (const Symbol& sym) { // Don't write over connections! Connection values are written into // our layer when the earlier layer is run, as part of its code. So // we just don't need to initialize it here at all. if (sym.valuesource() == Symbol::ConnectedVal && !sym.typespec().is_closure_based()) return; if (sym.typespec().is_closure_based() && sym.symtype() == SymTypeGlobal) return; int arraylen = std::max (1, sym.typespec().arraylength()); // Closures need to get their storage before anything can be // assigned to them. Unless they are params, in which case we took // care of it in the group entry point. if (sym.typespec().is_closure_based() && sym.symtype() != SymTypeParam && sym.symtype() != SymTypeOutputParam) { llvm_assign_zero (sym); return; } if ((sym.symtype() == SymTypeLocal || sym.symtype() == SymTypeTemp) && shadingsys().debug_uninit()) { // Handle the "debug uninitialized values" case bool isarray = sym.typespec().is_array(); int alen = isarray ? sym.typespec().arraylength() : 1; llvm::Value *u = NULL; if (sym.typespec().is_closure_based()) { // skip closures } else if (sym.typespec().is_floatbased()) u = ll.constant (std::numeric_limits<float>::quiet_NaN()); else if (sym.typespec().is_int_based()) u = ll.constant (std::numeric_limits<int>::min()); else if (sym.typespec().is_string_based()) u = ll.constant (Strings::uninitialized_string); if (u) { for (int a = 0; a < alen; ++a) { llvm::Value *aval = isarray ? ll.constant(a) : NULL; for (int c = 0; c < (int)sym.typespec().aggregate(); ++c) llvm_store_value (u, sym, 0, aval, c); } } return; } if ((sym.symtype() == SymTypeLocal || sym.symtype() == SymTypeTemp) && sym.typespec().is_string_based()) { // Strings are pointers. Can't take any chance on leaving // local/tmp syms uninitialized. llvm_assign_zero (sym); return; // we're done, the parts below are just for params } ASSERT_MSG (sym.symtype() == SymTypeParam || sym.symtype() == SymTypeOutputParam, "symtype was %d, data type was %s", (int)sym.symtype(), sym.typespec().c_str()); if (sym.has_init_ops() && sym.valuesource() == Symbol::DefaultVal) { // Handle init ops. build_llvm_code (sym.initbegin(), sym.initend()); } else if (! sym.lockgeom() && ! sym.typespec().is_closure()) { // geometrically-varying param; memcpy its default value TypeDesc t = sym.typespec().simpletype(); ll.op_memcpy (llvm_void_ptr (sym), ll.constant_ptr (sym.data()), t.size(), t.basesize() /*align*/); if (sym.has_derivs()) llvm_zero_derivs (sym); } else { // Use default value int num_components = sym.typespec().simpletype().aggregate; TypeSpec elemtype = sym.typespec().elementtype(); for (int a = 0, c = 0; a < arraylen; ++a) { llvm::Value *arrind = sym.typespec().is_array() ? ll.constant(a) : NULL; if (sym.typespec().is_closure_based()) continue; for (int i = 0; i < num_components; ++i, ++c) { // Fill in the constant val llvm::Value* init_val = 0; if (elemtype.is_floatbased()) init_val = ll.constant (((float*)sym.data())[c]); else if (elemtype.is_string()) init_val = ll.constant (((ustring*)sym.data())[c]); else if (elemtype.is_int()) init_val = ll.constant (((int*)sym.data())[c]); ASSERT (init_val); llvm_store_value (init_val, sym, 0, arrind, i); } } if (sym.has_derivs()) llvm_zero_derivs (sym); } // Handle interpolated params. // FIXME -- really, we shouldn't assign defaults or run init ops if // the values are interpolated. The perf hit is probably small, since // there are so few interpolated params, but we should come back and // fix this later. if ((sym.symtype() == SymTypeParam || sym.symtype() == SymTypeOutputParam) && ! sym.lockgeom()) { std::vector<llvm::Value*> args; args.push_back (sg_void_ptr()); args.push_back (ll.constant (sym.name())); args.push_back (ll.constant (sym.typespec().simpletype())); args.push_back (ll.constant ((int) sym.has_derivs())); args.push_back (llvm_void_ptr (sym)); ll.call_function ("osl_bind_interpolated_param", &args[0], args.size()); } }
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 (); }
void RuntimeOptimizer::llvm_assign_initial_value (const Symbol& sym) { // Don't write over connections! Connection values are written into // our layer when the earlier layer is run, as part of its code. So // we just don't need to initialize it here at all. if (sym.valuesource() == Symbol::ConnectedVal && !sym.typespec().is_closure_based()) return; if (sym.typespec().is_closure_based() && sym.symtype() == SymTypeGlobal) return; int arraylen = std::max (1, sym.typespec().arraylength()); // Closures need to get their storage before anything can be // assigned to them. Unless they are params, in which case we took // care of it in the group entry point. if (sym.typespec().is_closure_based() && sym.symtype() != SymTypeParam && sym.symtype() != SymTypeOutputParam) { llvm_assign_zero (sym); } if (sym.symtype() != SymTypeParam && sym.symtype() != SymTypeOutputParam && sym.symtype() != SymTypeConst && sym.typespec().is_string_based()) { // Strings are pointers. Can't take any chance on leaving local/tmp // syms uninitialized. DASSERT (sym.symtype() == SymTypeLocal || sym.symtype() == SymTypeTemp); llvm_assign_zero (sym); return; // we're done, the parts below are just for params } if (sym.has_init_ops() && sym.valuesource() == Symbol::DefaultVal) { // Handle init ops. build_llvm_code (sym.initbegin(), sym.initend()); } else { // Use default value int num_components = sym.typespec().simpletype().aggregate; for (int a = 0, c = 0; a < arraylen; ++a) { llvm::Value *arrind = sym.typespec().is_array() ? llvm_constant(a) : NULL; if (sym.typespec().is_closure_based()) continue; for (int i = 0; i < num_components; ++i, ++c) { // Fill in the constant val llvm::Value* init_val = 0; TypeSpec elemtype = sym.typespec().elementtype(); if (elemtype.is_floatbased()) init_val = llvm_constant (((float*)sym.data())[c]); else if (elemtype.is_string()) init_val = llvm_constant (((ustring*)sym.data())[c]); else if (elemtype.is_int()) init_val = llvm_constant (((int*)sym.data())[c]); ASSERT (init_val); llvm_store_value (init_val, sym, 0, arrind, i); } } if (sym.has_derivs()) llvm_zero_derivs (sym); } // Handle interpolated params. // FIXME -- really, we shouldn't assign defaults or run init ops if // the values are interpolated. The perf hit is probably small, since // there are so few interpolated params, but we should come back and // fix this later. if ((sym.symtype() == SymTypeParam || sym.symtype() == SymTypeOutputParam) && ! sym.lockgeom()) { std::vector<llvm::Value*> args; args.push_back (sg_void_ptr()); args.push_back (llvm_constant (sym.name())); args.push_back (llvm_constant (sym.typespec().simpletype())); args.push_back (llvm_constant ((int) sym.has_derivs())); args.push_back (llvm_void_ptr (sym)); llvm_call_function ("osl_bind_interpolated_param", &args[0], args.size()); } }