From 35b12e4420a59fc04b4fb46388e5d0a575ea8c60 Mon Sep 17 00:00:00 2001 From: Christopher Kulla Date: Sun, 27 Oct 2024 16:57:11 -0700 Subject: [PATCH] Reduce shared dependencies between liboslcomp and liboslexec (#1890) * Reduce shared dependencies between liboslcomp and liboslexec by extracting some common utility functions to more standalone classes Signed-off-by: Chris Kulla --- src/include/osl_pvt.h | 36 +++++ src/liboslcomp/ast.cpp | 13 +- src/liboslcomp/ast.h | 5 - src/liboslcomp/codegen.cpp | 4 +- src/liboslcomp/oslcomp.cpp | 106 +------------ src/liboslcomp/oslcomp_pvt.h | 31 +--- src/liboslcomp/symtab.cpp | 85 ++++++++++- src/liboslcomp/typecheck.cpp | 180 ++--------------------- src/liboslexec/CMakeLists.txt | 8 - src/liboslexec/batched_llvm_instance.cpp | 2 +- src/liboslexec/llvm_instance.cpp | 4 +- src/liboslexec/master.cpp | 2 +- src/liboslexec/runtimeoptimize.cpp | 14 +- src/liboslexec/typespec.cpp | 160 ++++++++++++++++++++ src/osl.imageio/CMakeLists.txt | 2 +- src/osltoy/CMakeLists.txt | 2 +- src/testshade/CMakeLists.txt | 4 +- 17 files changed, 312 insertions(+), 346 deletions(-) diff --git a/src/include/osl_pvt.h b/src/include/osl_pvt.h index 8b723374c..10530b62f 100644 --- a/src/include/osl_pvt.h +++ b/src/include/osl_pvt.h @@ -98,6 +98,10 @@ class TypeSpec { /// is not responsible for freeing the characters. const char* c_str() const; + /// Return the c_str giving a human-readable name of a type, fully + /// accounting for exotic types like structs, etc. + const char* type_c_str() const; + /// Stream output friend std::ostream& operator<<(std::ostream& o, const TypeSpec& t) { @@ -373,6 +377,28 @@ class TypeSpec { && (src.is_float() || src.is_int())); } + /// Given a pointer to a type code string that we use for argument + /// checking ("p", "v", etc.) return the TypeSpec of the first type + /// described by the string (UNKNOWN if it couldn't be recognized). + /// If 'advance' is non-NULL, set *advance to the number of + /// characters taken by the first code so the caller can advance + /// their pointer to the next code in the string. + static TypeSpec type_from_code(const char* code, int* advance = nullptr); + + /// Return the argument checking code ("p", "v", etc.) corresponding + /// to the type. + std::string code_from_type() const; + + /// Take a type code string (possibly containing many types) + /// and turn it into a human-readable string. + static std::string typelist_from_code(const char* code); + + /// Take a type code string (possibly containing many types) and + /// turn it into a TypeSpec vector. + static void typespecs_from_codes(const char* code, + std::vector& types); + + private: TypeDesc m_simple; ///< Data if it's a simple type short m_structure; ///< 0 is not a structure, >=1 for structure id @@ -1125,6 +1151,16 @@ class Opcode { typedef std::vector OpcodeVec; +/// Called after code is generated, this function loops over all the ops +/// and figures out the lifetimes of all variables, based on whether the +/// args in each op are read or written. This function is used both in +/// the compiler and the runtime optimizer. +void +track_variable_lifetimes_main(const OpcodeVec& ircode, + const SymbolPtrVec& opargs, + const SymbolPtrVec& allsyms, + std::vector* bblock_ids = nullptr); + }; // namespace pvt diff --git a/src/liboslcomp/ast.cpp b/src/liboslcomp/ast.cpp index a8e5d75e4..4b54fa41a 100644 --- a/src/liboslcomp/ast.cpp +++ b/src/liboslcomp/ast.cpp @@ -275,15 +275,6 @@ ASTNode::printchildren(std::ostream& out, int indentlevel) const } - -const char* -ASTNode::type_c_str(const TypeSpec& type) const -{ - return m_compiler->type_c_str(type); -} - - - void ASTNode::list_to_vec(const ref& A, std::vector& vec) { @@ -401,14 +392,14 @@ ASTfunction_declaration::ASTfunction_declaration(OSLCompilerImpl* comp, // Build up the argument signature for this declared function m_typespec = type; - std::string argcodes = m_compiler->code_from_type(m_typespec); + std::string argcodes = m_typespec.code_from_type(); for (ASTNode* arg = form; arg; arg = arg->nextptr()) { const TypeSpec& t(arg->typespec()); if (t == TypeSpec() /* UNKNOWN */) { m_typespec = TypeDesc::UNKNOWN; return; } - argcodes += m_compiler->code_from_type(t); + argcodes += t.code_from_type(); OSL_ASSERT(arg->nodetype() == variable_declaration_node); ASTvariable_declaration* v = (ASTvariable_declaration*)arg; if (v->init()) diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index 7696753af..b057f8dfd 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -349,11 +349,6 @@ class ASTNode : public OIIO::RefCnt { /// coercion if acceptfloat is false. Symbol* coerce(Symbol* sym, const TypeSpec& type, bool acceptfloat = false); - /// Return the c_str giving a human-readable name of a type, fully - /// accounting for exotic types like structs, etc. - /// N.B.: just conveniently wraps the compiler's identical method. - const char* type_c_str(const TypeSpec& type) const; - /// Assign the struct variable named by srcsym to the struct /// variable named by dstsym by assigning each field individually. /// In the case of dstsym naming an array of structs, arrayindex diff --git a/src/liboslcomp/codegen.cpp b/src/liboslcomp/codegen.cpp index 8afba9deb..16dbbbcdf 100644 --- a/src/liboslcomp/codegen.cpp +++ b/src/liboslcomp/codegen.cpp @@ -1812,8 +1812,8 @@ ASTfunction_call::codegen(Symbol* dest) std::vector polyargs; const char* param_argcodes = func()->argcodes().c_str(); int len; - m_compiler->type_from_code(param_argcodes, &len); // skip ret type - m_compiler->typespecs_from_codes(param_argcodes + len, polyargs); + TypeSpec::type_from_code(param_argcodes, &len); // skip ret type + TypeSpec::typespecs_from_codes(param_argcodes + len, polyargs); // Generate code for all the individual arguments. Remember the // individual indices for arguments that are array elements or diff --git a/src/liboslcomp/oslcomp.cpp b/src/liboslcomp/oslcomp.cpp index 1c29aee50..f4c4803d9 100644 --- a/src/liboslcomp/oslcomp.cpp +++ b/src/liboslcomp/oslcomp.cpp @@ -85,11 +85,6 @@ OSLCompiler::output_filename() const namespace pvt { // OSL::pvt -static ustring op_for("for"); -static ustring op_while("while"); -static ustring op_dowhile("dowhile"); - - OSLCompilerImpl::OSLCompilerImpl(ErrorHandler* errhandler) : m_errhandler(errhandler ? errhandler : &ErrorHandler::default_handler()) @@ -769,7 +764,7 @@ OSLCompilerImpl::write_oso_symbol(const Symbol* sym) if (i > 0) fieldlist += ","; fieldlist += structspec->field(i).name.string(); - signature += code_from_type(structspec->field(i).type); + signature += structspec->field(i).type.code_from_type(); } osofmt( "{}%struct{{\"{}\"}} %structfields{{{}}} %structfieldtypes{{\"{}\"}} %structnfields{{{}}}", @@ -1068,19 +1063,6 @@ OSLCompilerImpl::pop_nesting(bool isloop) } - -const char* -OSLCompilerImpl::type_c_str(const TypeSpec& type) const -{ - if (type.is_structure()) - return ustring::fmtformat("struct {}", type.structspec()->name()) - .c_str(); - else - return type.c_str(); -} - - - void OSLCompilerImpl::struct_field_pair(Symbol* sym1, Symbol* sym2, int fieldnum, Symbol*& field1, Symbol*& field2) @@ -1162,92 +1144,6 @@ OSLCompilerImpl::check_for_illegal_writes() -/// Called after code is generated, this function loops over all the ops -/// and figures out the lifetimes of all variables, based on whether the -/// args in each op are read or written. -void -OSLCompilerImpl::track_variable_lifetimes(const OpcodeVec& code, - const SymbolPtrVec& opargs, - const SymbolPtrVec& allsyms, - std::vector* bblockids) -{ - // Clear the lifetimes for all symbols - for (auto&& s : allsyms) - s->clear_rw(); - - // Keep track of the nested loops we're inside. We track them by pairs - // of begin/end instruction numbers for that loop body, including - // conditional evaluation (skip the initialization). Note that the end - // is inclusive. We use this vector of ranges as a stack. - typedef std::pair intpair; - std::vector loop_bounds; - - // For each op, mark its arguments as being used at that op - int opnum = 0; - for (auto&& op : code) { - if (op.opname() == op_for || op.opname() == op_while - || op.opname() == op_dowhile) { - // If this is a loop op, we need to mark its control variable - // (the only arg) as used for the duration of the loop! - OSL_DASSERT(op.nargs() == 1); // loops should have just one arg - SymbolPtr s = opargs[op.firstarg()]; - int loopcond = op.jump(0); // after initialization, before test - int loopend = op.farthest_jump() - 1; // inclusive end - s->mark_rw(opnum + 1, true, true); - s->mark_rw(loopend, true, true); - // Also push the loop bounds for this loop - loop_bounds.push_back(std::make_pair(loopcond, loopend)); - } - - // Some work to do for each argument to the op... - for (int a = 0; a < op.nargs(); ++a) { - SymbolPtr s = opargs[op.firstarg() + a]; - OSL_DASSERT(s->dealias() == s); // Make sure it's de-aliased - - // Mark that it's read and/or written for this op - bool readhere = op.argread(a); - bool writtenhere = op.argwrite(a); - s->mark_rw(opnum, readhere, writtenhere); - - // Adjust lifetimes of symbols whose values need to be preserved - // between loop iterations. - for (auto oprange : loop_bounds) { - int loopcond = oprange.first; - int loopend = oprange.second; - OSL_DASSERT(s->firstuse() <= loopend); - // Special case: a temp or local, even if written inside a - // loop, if it's entire lifetime is within one basic block - // and it's strictly written before being read, then its - // lifetime is truly local and doesn't need to be expanded - // for the duration of the loop. - if (bblockids - && (s->symtype() == SymTypeLocal - || s->symtype() == SymTypeTemp) - && (*bblockids)[s->firstuse()] == (*bblockids)[s->lastuse()] - && s->lastwrite() < s->firstread()) { - continue; - } - // Syms written before or inside the loop, and referenced - // inside or after the loop, need to preserve their value - // for the duration of the loop. We know it's referenced - // inside the loop because we're here examining it! - if (s->firstwrite() <= loopend) { - s->mark_rw(loopcond, readhere, writtenhere); - s->mark_rw(loopend, readhere, writtenhere); - } - } - } - - ++opnum; // Advance to the next op index - - // Pop any loop bounds for loops we've just exited - while (!loop_bounds.empty() && loop_bounds.back().second < opnum) - loop_bounds.pop_back(); - } -} - - - // This has O(n^2) memory usage, so only for debugging //#define DEBUG_SYMBOL_DEPENDENCIES diff --git a/src/liboslcomp/oslcomp_pvt.h b/src/liboslcomp/oslcomp_pvt.h index 3066965ea..27a1cedb3 100644 --- a/src/liboslcomp/oslcomp_pvt.h +++ b/src/liboslcomp/oslcomp_pvt.h @@ -192,27 +192,6 @@ class OSLCompilerImpl { } bool declaring_shader_formals() const { return m_declaring_shader_formals; } - /// Given a pointer to a type code string that we use for argument - /// checking ("p", "v", etc.) return the TypeSpec of the first type - /// described by the string (UNKNOWN if it couldn't be recognized). - /// If 'advance' is non-NULL, set *advance to the number of - /// characters taken by the first code so the caller can advance - /// their pointer to the next code in the string. - static TypeSpec type_from_code(const char* code, int* advance = NULL); - - /// Return the argument checking code ("p", "v", etc.) corresponding - /// to the type. - std::string code_from_type(TypeSpec type) const; - - /// Take a type code string (possibly containing many types) - /// and turn it into a human-readable string. - std::string typelist_from_code(const char* code) const; - - /// Take a type code string (possibly containing many types) and - /// turn it into a TypeSpec vector. - void typespecs_from_codes(const char* code, - std::vector& types) const; - /// Emit a single IR opcode -- append one op to the list of /// intermediate code, returning the label (address) of the new op. int emitcode(const char* opname, size_t nargs, Symbol** args, @@ -326,10 +305,6 @@ class OSLCompilerImpl { return loops ? m_loop_nesting : m_total_nesting; } - /// Return the c_str giving a human-readable name of a type, fully - /// accounting for exotic types like structs, etc. - const char* type_c_str(const TypeSpec& type) const; - /// Given symbols sym1 and sym2, both the same kind of struct, and the /// index of a field we're interested, find the symbols that represent /// that field in the each sym and place them in field1 and field2, @@ -346,10 +321,6 @@ class OSLCompilerImpl { ustring sym1, ustring sym2, Symbol*& field1, Symbol*& field2); - static void track_variable_lifetimes(const OpcodeVec& ircode, - const SymbolPtrVec& opargs, - const SymbolPtrVec& allsyms, - std::vector* bblock_ids = NULL); static void coalesce_temporaries(SymbolPtrVec& symtab); ustring main_filename() const { return m_main_filename; } @@ -399,7 +370,7 @@ class OSLCompilerImpl { void track_variable_lifetimes() { - track_variable_lifetimes(m_ircode, m_opargs, symtab().allsyms()); + track_variable_lifetimes_main(m_ircode, m_opargs, symtab().allsyms()); } void track_variable_dependencies(); diff --git a/src/liboslcomp/symtab.cpp b/src/liboslcomp/symtab.cpp index a1ed1c70c..da768f39a 100644 --- a/src/liboslcomp/symtab.cpp +++ b/src/liboslcomp/symtab.cpp @@ -326,7 +326,7 @@ SymbolTable::print() const char* args = f->argcodes().c_str(); int advance = 0; args += advance; - std::cout << " function (" << m_comp.typelist_from_code(args) + std::cout << " function (" << TypeSpec::typelist_from_code(args) << ") "; } std::cout << "\n"; @@ -334,6 +334,89 @@ SymbolTable::print() std::cout << "\n"; } +static ustring op_for("for"); +static ustring op_while("while"); +static ustring op_dowhile("dowhile"); + +void +track_variable_lifetimes_main(const OpcodeVec& code, const SymbolPtrVec& opargs, + const SymbolPtrVec& allsyms, + std::vector* bblockids) +{ + // Clear the lifetimes for all symbols + for (auto&& s : allsyms) + s->clear_rw(); + + // Keep track of the nested loops we're inside. We track them by pairs + // of begin/end instruction numbers for that loop body, including + // conditional evaluation (skip the initialization). Note that the end + // is inclusive. We use this vector of ranges as a stack. + typedef std::pair intpair; + std::vector loop_bounds; + + // For each op, mark its arguments as being used at that op + int opnum = 0; + for (auto&& op : code) { + if (op.opname() == op_for || op.opname() == op_while + || op.opname() == op_dowhile) { + // If this is a loop op, we need to mark its control variable + // (the only arg) as used for the duration of the loop! + OSL_DASSERT(op.nargs() == 1); // loops should have just one arg + SymbolPtr s = opargs[op.firstarg()]; + int loopcond = op.jump(0); // after initialization, before test + int loopend = op.farthest_jump() - 1; // inclusive end + s->mark_rw(opnum + 1, true, true); + s->mark_rw(loopend, true, true); + // Also push the loop bounds for this loop + loop_bounds.push_back(std::make_pair(loopcond, loopend)); + } + + // Some work to do for each argument to the op... + for (int a = 0; a < op.nargs(); ++a) { + SymbolPtr s = opargs[op.firstarg() + a]; + OSL_DASSERT(s->dealias() == s); // Make sure it's de-aliased + + // Mark that it's read and/or written for this op + bool readhere = op.argread(a); + bool writtenhere = op.argwrite(a); + s->mark_rw(opnum, readhere, writtenhere); + + // Adjust lifetimes of symbols whose values need to be preserved + // between loop iterations. + for (auto oprange : loop_bounds) { + int loopcond = oprange.first; + int loopend = oprange.second; + OSL_DASSERT(s->firstuse() <= loopend); + // Special case: a temp or local, even if written inside a + // loop, if it's entire lifetime is within one basic block + // and it's strictly written before being read, then its + // lifetime is truly local and doesn't need to be expanded + // for the duration of the loop. + if (bblockids + && (s->symtype() == SymTypeLocal + || s->symtype() == SymTypeTemp) + && (*bblockids)[s->firstuse()] == (*bblockids)[s->lastuse()] + && s->lastwrite() < s->firstread()) { + continue; + } + // Syms written before or inside the loop, and referenced + // inside or after the loop, need to preserve their value + // for the duration of the loop. We know it's referenced + // inside the loop because we're here examining it! + if (s->firstwrite() <= loopend) { + s->mark_rw(loopcond, readhere, writtenhere); + s->mark_rw(loopend, readhere, writtenhere); + } + } + } + + ++opnum; // Advance to the next op index + + // Pop any loop bounds for loops we've just exited + while (!loop_bounds.empty() && loop_bounds.back().second < opnum) + loop_bounds.pop_back(); + } +} }; // namespace pvt diff --git a/src/liboslcomp/typecheck.cpp b/src/liboslcomp/typecheck.cpp index be66fe057..f933464ec 100644 --- a/src/liboslcomp/typecheck.cpp +++ b/src/liboslcomp/typecheck.cpp @@ -402,7 +402,7 @@ ASTunary_expression::typecheck(TypeSpec expected) for (FunctionSymbol* f = m_function_overload; f; f = f->nextpoly()) { const char* code = f->argcodes().c_str(); int advance; - TypeSpec returntype = m_compiler->type_from_code(code, &advance); + TypeSpec returntype = TypeSpec::type_from_code(code, &advance); code += advance; if (code[0] && check_simple_arg(t, code, true) && !code[0]) { return m_typespec = returntype; @@ -475,7 +475,7 @@ ASTbinary_expression::typecheck(TypeSpec expected) for (FunctionSymbol* f = m_function_overload; f; f = f->nextpoly()) { const char* code = f->argcodes().c_str(); int advance; - TypeSpec returntype = m_compiler->type_from_code(code, &advance); + TypeSpec returntype = TypeSpec::type_from_code(code, &advance); code += advance; if (code[0] && check_simple_arg(l, code, true) && code[0] && check_simple_arg(r, code, true) && !code[0]) { // NOSONAR @@ -710,7 +710,7 @@ ASTtype_constructor::typecheck(TypeSpec expected, bool report, bool bind) bool coerce = co; for (const char** pat = patterns; *pat; ++pat) { const char* code = *pat; - if (check_arglist(type_c_str(expected), args(), code + 1, coerce, + if (check_arglist(expected.type_c_str(), args(), code + 1, coerce, bind)) return expected; } @@ -1041,7 +1041,7 @@ ASTNode::check_simple_arg(const TypeSpec& argtype, const char*& formals, bool coerce) { int advance; - TypeSpec formaltype = m_compiler->type_from_code(formals, &advance); + TypeSpec formaltype = TypeSpec::type_from_code(formals, &advance); formals += advance; // std::cerr << "\targ is " << argtype.string() // << ", formal is " << formaltype.string() << "\n"; @@ -1100,7 +1100,7 @@ ASTNode::check_arglist(const char* /*funcname*/, ASTNode::ref arg, if (arg->nodetype() == compound_initializer_node) { int advance; // Get the TypeSpec from the argument string. - TypeSpec formaltype = m_compiler->type_from_code(formals, &advance); + TypeSpec formaltype = TypeSpec::type_from_code(formals, &advance); // See if the initlist can be used to construct a formaltype. ASTcompound_initializer::TypeAdjuster ta( @@ -1532,7 +1532,7 @@ class CandidateFunctions { int advance; const char* formals = func->argcodes().c_str(); - TypeSpec rtype = m_compiler->type_from_code(formals, &advance); + TypeSpec rtype = TypeSpec::type_from_code(formals, &advance); formals += advance; InitBindings bindings; @@ -1575,7 +1575,7 @@ class CandidateFunctions { return kNoMatch; TypeSpec argtype; - TypeSpec formaltype = m_compiler->type_from_code(formals, &advance); + TypeSpec formaltype = TypeSpec::type_from_code(formals, &advance); formals += advance; if (arg->nodetype() == ASTNode::compound_initializer_node) { @@ -1694,14 +1694,14 @@ class CandidateFunctions { { int advance; const char* formals = sym->argcodes().c_str(); - TypeSpec returntype = m_compiler->type_from_code(formals, &advance); + TypeSpec returntype = TypeSpec::type_from_code(formals, &advance); formals += advance; std::string msg = " "; if (ASTNode* decl = sym->node()) msg += Strutil::fmt::format("{}:{}\t", decl->sourcefile(), decl->sourceline()); msg += Strutil::fmt::format("{} {} ({})\n", returntype, sym->name(), - m_compiler->typelist_from_code(formals)); + TypeSpec::typelist_from_code(formals)); return msg; } @@ -1900,7 +1900,7 @@ class LegacyOverload { for (FunctionSymbol* poly = m_root; poly; poly = poly->nextpoly()) { const char* code = poly->argcodes().c_str(); int advance; - TypeSpec returntype = m_compiler->type_from_code(code, &advance); + TypeSpec returntype = TypeSpec::type_from_code(code, &advance); code += advance; if ((m_func->*m_check_arglist)(name, m_func->args(), code, coerceargs, false)) { @@ -2216,7 +2216,7 @@ OSLCompilerImpl::initialize_builtin_funcs() ustring poly(builtin_func_args[i + j]); Symbol* last = symtab().clash(funcname); OSL_DASSERT(last == NULL || last->symtype() == SymTypeFunction); - TypeSpec rettype = type_from_code(poly.c_str()); + TypeSpec rettype = TypeSpec::type_from_code(poly.c_str()); FunctionSymbol* f = new FunctionSymbol(funcname, rettype); f->nextpoly((FunctionSymbol*)last); f->argcodes(poly); @@ -2231,164 +2231,6 @@ OSLCompilerImpl::initialize_builtin_funcs() } - -TypeSpec -OSLCompilerImpl::type_from_code(const char* code, int* advance) -{ - TypeSpec t; - int i = 0; - switch (code[i]) { - case 'i': t = TypeInt; break; - case 'f': t = TypeFloat; break; - case 'c': t = TypeColor; break; - case 'p': t = TypePoint; break; - case 'v': t = TypeVector; break; - case 'n': t = TypeNormal; break; - case 'm': t = TypeMatrix; break; - case 's': t = TypeString; break; - case 'h': t = OSL::TypeUInt64; break; // ustringhash_pod - case 'x': t = TypeDesc(TypeDesc::NONE); break; - case 'X': t = TypeDesc(TypeDesc::PTR); break; - case 'L': t = TypeDesc(TypeDesc::LONGLONG); break; - case 'C': // color closure - t = TypeSpec(TypeColor, true); - break; - case 'S': // structure - // Following the 'S' is the numeric structure ID - t = TypeSpec("struct", atoi(code + i + 1)); - // Skip to the last digit - while (isdigit(code[i + 1])) - ++i; - break; - case '?': break; // anything will match, so keep 'UNKNOWN' - case '*': break; // anything will match, so keep 'UNKNOWN' - case '.': break; // anything will match, so keep 'UNKNOWN' - default: - OSL_DASSERT_MSG(0, "Don't know how to decode type code '%d'", - (int)code[0]); - if (advance) - *advance = 1; - return TypeSpec(); - } - ++i; - - if (code[i] == '[') { - ++i; - t.make_array(-1); // signal arrayness, unknown length - if (isdigit(code[i]) || code[i] == ']') { - if (isdigit(code[i])) - t.make_array(atoi(code + i)); - while (isdigit(code[i])) - ++i; - if (code[i] == ']') - ++i; - } - } - - if (advance) - *advance = i; - return t; -} - - - -std::string -OSLCompilerImpl::typelist_from_code(const char* code) const -{ - std::string ret; - while (*code) { - // Handle some special cases - int advance = 1; - if (ret.length()) - ret += ", "; - if (*code == '.') { - ret += "..."; - } else if (*code == 'T') { - ret += "..."; - } else if (*code == '?') { - ret += ""; - } else { - TypeSpec t = type_from_code(code, &advance); - ret += type_c_str(t); - } - code += advance; - if (*code == '[') { - ret += "[]"; - ++code; - while (isdigit(*code)) - ++code; - if (*code == ']') - ++code; - } - } - - return ret; -} - - - -std::string -OSLCompilerImpl::code_from_type(TypeSpec type) const -{ - std::string out; - TypeDesc elem = type.elementtype().simpletype(); - if (type.is_structure() || type.is_structure_array()) { - out = Strutil::fmt::format("S{}", type.structure()); - } else if (type.is_closure() || type.is_closure_array()) { - out = 'C'; - } else { - if (elem == TypeInt) - out = 'i'; - else if (elem == TypeFloat) - out = 'f'; - else if (elem == TypeColor) - out = 'c'; - else if (elem == TypePoint) - out = 'p'; - else if (elem == TypeVector) - out = 'v'; - else if (elem == TypeNormal) - out = 'n'; - else if (elem == TypeMatrix) - out = 'm'; - else if (elem == TypeString) - out = 's'; - else if (elem == TypeDesc::NONE) - out = 'x'; - else { - out = 'x'; - // This only happens in error circumstances. Seems safe to - // return the code for 'void' and hope everything sorts itself - // out with the downstream errors. - } - } - - if (type.is_array()) { - if (type.is_unsized_array()) - out += "[]"; - else - out += Strutil::fmt::format("[{}]", type.arraylength()); - } - - return out; -} - - - -void -OSLCompilerImpl::typespecs_from_codes(const char* code, - std::vector& types) const -{ - types.clear(); - while (code && *code) { - int advance; - types.push_back(type_from_code(code, &advance)); - code += advance; - } -} - - - }; // namespace pvt OSL_NAMESPACE_EXIT diff --git a/src/liboslexec/CMakeLists.txt b/src/liboslexec/CMakeLists.txt index de8fa7b4b..57cb7d59b 100644 --- a/src/liboslexec/CMakeLists.txt +++ b/src/liboslexec/CMakeLists.txt @@ -141,11 +141,7 @@ endif() if (BUILD_SHARED_LIBS) # oslcomp symbols used in oslexec list(APPEND lib_src - ../liboslcomp/ast.cpp - ../liboslcomp/codegen.cpp - ../liboslcomp/oslcomp.cpp ../liboslcomp/symtab.cpp - ../liboslcomp/typecheck.cpp ) endif () @@ -153,9 +149,6 @@ file (GLOB exec_headers "*.h") file (GLOB compiler_headers "../liboslcomp/*.h") FLEX_BISON ( osolex.l osogram.y oso lib_src exec_headers ) -if (BUILD_SHARED_LIBS) - FLEX_BISON ( ../liboslcomp/osllex.l ../liboslcomp/oslgram.y osl lib_src compiler_headers ) -endif() set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS" ) @@ -581,7 +574,6 @@ target_link_libraries (${local_lib} PRIVATE pugixml::pugixml ${CMAKE_DL_LIBS} - ${CLANG_LIBRARIES} ${LLVM_LIBRARIES} ${LLVM_LDFLAGS} ${LLVM_SYSTEM_LIBRARIES} ) diff --git a/src/liboslexec/batched_llvm_instance.cpp b/src/liboslexec/batched_llvm_instance.cpp index 4e5a46c33..a8fb3eb8c 100644 --- a/src/liboslexec/batched_llvm_instance.cpp +++ b/src/liboslexec/batched_llvm_instance.cpp @@ -154,7 +154,7 @@ possibly_wide_type_from_code(const char* code, int* advance, bool& is_uniform) is_uniform = true; } - TypeSpec t = OSLCompilerImpl::type_from_code(code + i, advance); + TypeSpec t = TypeSpec::type_from_code(code + i, advance); if (advance) *advance += i; diff --git a/src/liboslexec/llvm_instance.cpp b/src/liboslexec/llvm_instance.cpp index c9bedad28..8554925dd 100644 --- a/src/liboslexec/llvm_instance.cpp +++ b/src/liboslexec/llvm_instance.cpp @@ -1899,11 +1899,11 @@ BackendLLVM::initialize_llvm_group() bool varargs = false; const char* types = i->second.argtypes; int advance; - TypeSpec rettype = OSLCompilerImpl::type_from_code(types, &advance); + TypeSpec rettype = TypeSpec::type_from_code(types, &advance); types += advance; std::vector params; while (*types) { - TypeSpec t = OSLCompilerImpl::type_from_code(types, &advance); + TypeSpec t = TypeSpec::type_from_code(types, &advance); if (t.simpletype().basetype == TypeDesc::UNKNOWN) { OSL_DASSERT(*types == '*'); if (*types == '*') diff --git a/src/liboslexec/master.cpp b/src/liboslexec/master.cpp index 6c257ac38..9cc152f76 100644 --- a/src/liboslexec/master.cpp +++ b/src/liboslexec/master.cpp @@ -163,7 +163,7 @@ ShaderMaster::resolve_syms() oparg_ptrs.reserve(m_args.size()); for (auto&& a : m_args) oparg_ptrs.push_back(symbol(a)); - OSLCompilerImpl::track_variable_lifetimes(m_ops, oparg_ptrs, allsymptrs); + track_variable_lifetimes_main(m_ops, oparg_ptrs, allsymptrs); // Figure out which ray types are queried m_raytype_queries = 0; diff --git a/src/liboslexec/runtimeoptimize.cpp b/src/liboslexec/runtimeoptimize.cpp index 8e91a9d08..2248337db 100644 --- a/src/liboslexec/runtimeoptimize.cpp +++ b/src/liboslexec/runtimeoptimize.cpp @@ -46,6 +46,7 @@ static ustring u_calculatenormal("calculatenormal"); static ustring u_flipHandedness("flipHandedness"); static ustring u_N("N"); static ustring u_I("I"); +static ustring main_method_name("___main___"); OSL_NAMESPACE_ENTER @@ -539,9 +540,8 @@ RuntimeOptimizer::insert_code(int opnum, ustring opname, { OpcodeVec& code(inst()->ops()); std::vector& opargs(inst()->args()); - ustring method = (opnum < (int)code.size()) - ? code[opnum].method() - : OSLCompilerImpl::main_method_name(); + ustring method = (opnum < (int)code.size()) ? code[opnum].method() + : main_method_name; int nargs = args_to_add.size(); Opcode op(opname, method, opargs.size(), nargs); code.insert(code.begin() + opnum, op); @@ -675,7 +675,7 @@ RuntimeOptimizer::insert_useparam(size_t opnum, code[opnum].method(code[opnum + 1].method()); } else { // If there IS no "next" instruction, just call it main - code[opnum].method(OSLCompilerImpl::main_method_name()); + code[opnum].method(main_method_name); } } @@ -746,7 +746,7 @@ RuntimeOptimizer::add_useparam(SymbolPtrVec& allsyms) params.push_back(opargs[argind]); // mark as already initialized unconditionally, if we do if (op_is_unconditionally_executed(opnum) - && op.method() == OSLCompilerImpl::main_method_name()) + && op.method() == main_method_name) s->initialized(true); } } @@ -2517,8 +2517,8 @@ RuntimeOptimizer::track_variable_lifetimes(const SymbolPtrVec& allsymptrs) if (m_bblockids.size() != inst()->ops().size()) find_basic_blocks(); - OSLCompilerImpl::track_variable_lifetimes(inst()->ops(), oparg_ptrs, - allsymptrs, &m_bblockids); + track_variable_lifetimes_main(inst()->ops(), oparg_ptrs, allsymptrs, + &m_bblockids); } diff --git a/src/liboslexec/typespec.cpp b/src/liboslexec/typespec.cpp index aa3889d15..1d0df3aca 100644 --- a/src/liboslexec/typespec.cpp +++ b/src/liboslexec/typespec.cpp @@ -76,6 +76,15 @@ TypeSpec::c_str() const } +const char* +TypeSpec::type_c_str() const +{ + if (is_structure()) + return ustring::fmtformat("struct {}", structspec()->name()).c_str(); + else + return c_str(); +} + int TypeSpec::structure_id(const char* name, bool add) @@ -109,6 +118,157 @@ TypeSpec::new_struct(StructSpec* n) return (int)m_structs.size() - 1; } +TypeSpec +TypeSpec::type_from_code(const char* code, int* advance) +{ + TypeSpec t; + int i = 0; + switch (code[i]) { + case 'i': t = TypeInt; break; + case 'f': t = TypeFloat; break; + case 'c': t = TypeColor; break; + case 'p': t = TypePoint; break; + case 'v': t = TypeVector; break; + case 'n': t = TypeNormal; break; + case 'm': t = TypeMatrix; break; + case 's': t = TypeString; break; + case 'h': t = OSL::TypeUInt64; break; // ustringhash_pod + case 'x': t = TypeDesc(TypeDesc::NONE); break; + case 'X': t = TypeDesc(TypeDesc::PTR); break; + case 'L': t = TypeDesc(TypeDesc::LONGLONG); break; + case 'C': // color closure + t = TypeSpec(TypeColor, true); + break; + case 'S': // structure + // Following the 'S' is the numeric structure ID + t = TypeSpec("struct", atoi(code + i + 1)); + // Skip to the last digit + while (isdigit(code[i + 1])) + ++i; + break; + case '?': break; // anything will match, so keep 'UNKNOWN' + case '*': break; // anything will match, so keep 'UNKNOWN' + case '.': break; // anything will match, so keep 'UNKNOWN' + default: + OSL_DASSERT_MSG(0, "Don't know how to decode type code '%d'", + (int)code[0]); + if (advance) + *advance = 1; + return TypeSpec(); + } + ++i; + + if (code[i] == '[') { + ++i; + t.make_array(-1); // signal arrayness, unknown length + if (isdigit(code[i]) || code[i] == ']') { + if (isdigit(code[i])) + t.make_array(atoi(code + i)); + while (isdigit(code[i])) + ++i; + if (code[i] == ']') + ++i; + } + } + + if (advance) + *advance = i; + return t; +} + +std::string +TypeSpec::typelist_from_code(const char* code) +{ + std::string ret; + while (*code) { + // Handle some special cases + int advance = 1; + if (ret.length()) + ret += ", "; + if (*code == '.') { + ret += "..."; + } else if (*code == 'T') { + ret += "..."; + } else if (*code == '?') { + ret += ""; + } else { + TypeSpec t = TypeSpec::type_from_code(code, &advance); + ret += t.type_c_str(); + } + code += advance; + if (*code == '[') { + ret += "[]"; + ++code; + while (isdigit(*code)) + ++code; + if (*code == ']') + ++code; + } + } + + return ret; +} + + + +std::string +TypeSpec::code_from_type() const +{ + std::string out; + TypeDesc elem = elementtype().simpletype(); + if (is_structure() || is_structure_array()) { + out = Strutil::fmt::format("S{}", structure()); + } else if (is_closure() || is_closure_array()) { + out = 'C'; + } else { + if (elem == TypeInt) + out = 'i'; + else if (elem == TypeFloat) + out = 'f'; + else if (elem == TypeColor) + out = 'c'; + else if (elem == TypePoint) + out = 'p'; + else if (elem == TypeVector) + out = 'v'; + else if (elem == TypeNormal) + out = 'n'; + else if (elem == TypeMatrix) + out = 'm'; + else if (elem == TypeString) + out = 's'; + else if (elem == TypeDesc::NONE) + out = 'x'; + else { + out = 'x'; + // This only happens in error circumstances. Seems safe to + // return the code for 'void' and hope everything sorts itself + // out with the downstream errors. + } + } + + if (is_array()) { + if (is_unsized_array()) + out += "[]"; + else + out += Strutil::fmt::format("[{}]", arraylength()); + } + + return out; +} + + + +void +TypeSpec::typespecs_from_codes(const char* code, std::vector& types) +{ + types.clear(); + while (code && *code) { + int advance; + types.push_back(TypeSpec::type_from_code(code, &advance)); + code += advance; + } +} bool diff --git a/src/osl.imageio/CMakeLists.txt b/src/osl.imageio/CMakeLists.txt index 70214bc3e..bf1768ff8 100644 --- a/src/osl.imageio/CMakeLists.txt +++ b/src/osl.imageio/CMakeLists.txt @@ -53,4 +53,4 @@ endmacro () add_oiio_plugin (oslinput.cpp - LINK_LIBRARIES oslexec) + LINK_LIBRARIES oslexec oslcomp) diff --git a/src/osltoy/CMakeLists.txt b/src/osltoy/CMakeLists.txt index 331a43986..a8c93a460 100644 --- a/src/osltoy/CMakeLists.txt +++ b/src/osltoy/CMakeLists.txt @@ -12,7 +12,7 @@ add_executable (osltoy ${osltoy_srcs}) set_target_properties (osltoy PROPERTIES FOLDER "Tools") target_link_libraries (osltoy PRIVATE - oslexec + oslexec oslcomp $ $ $ diff --git a/src/testshade/CMakeLists.txt b/src/testshade/CMakeLists.txt index d9343a231..7e95fdc9e 100644 --- a/src/testshade/CMakeLists.txt +++ b/src/testshade/CMakeLists.txt @@ -77,7 +77,7 @@ add_executable ( testshade ${testshade_srcs} testshademain.cpp ) target_link_libraries (testshade PRIVATE - oslexec oslquery) + oslexec oslquery oslcomp) install (TARGETS testshade RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) @@ -104,7 +104,7 @@ if (NOT CODECOV) target_link_libraries (libtestshade PRIVATE - oslexec oslquery) + oslexec oslquery oslcomp) set_target_properties (libtestshade PROPERTIES PREFIX "") install_targets ( libtestshade )