From 62ab57dcaa96587c9c7d014571c4b83da1181090 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Mon, 13 Mar 2023 09:58:11 -0700 Subject: [PATCH] oslc: simple constant folding of binary expressions on the oslc side The runtime optimization does a great job of constant folding, and we'd pushed it there because, with instance parameter values and shader network connections known, it can find so much more opportunity to optimize than we could in oslc. However, there is one pesky problem, which is that shader parameters that are initialized to even simple expressions such as 3+1 end up with "init ops", which although later constant folded by the time the shader is JITed, make it impossible to know the value via OSLQuery. So this patch just takes the simplest cases -- certain `int OP int` and `float OP float` expressions involving literal constants, and performs the operation as it's parsing the code. So int val = 3 + 1; actually just immediately is turned into `val = 4` instead of making an "add". To reiterate, the add would never have happened while executing the shader -- at runtime in the renderer, when it's time to optimize and JIT the shader, it would know it's a constant 4 value. This is strictly about making oslc directly output an .oso file that knows that parameter `val` has default value 4 instead of throwing up its hands and saying "it's math code that will be evaluated later." Signed-off-by: Larry Gritz --- src/cmake/testing.cmake | 1 + src/liboslcomp/ast.cpp | 63 +++++++++++++++++++++++ src/liboslcomp/ast.h | 4 ++ src/liboslcomp/oslgram.y | 36 +++++++------- testsuite/oslc-literalfold/ref/out.txt | 28 +++++++++++ testsuite/oslc-literalfold/run.py | 7 +++ testsuite/oslc-literalfold/test.osl | 69 ++++++++++++++++++++++++++ 7 files changed, 190 insertions(+), 18 deletions(-) create mode 100644 testsuite/oslc-literalfold/ref/out.txt create mode 100755 testsuite/oslc-literalfold/run.py create mode 100644 testsuite/oslc-literalfold/test.osl diff --git a/src/cmake/testing.cmake b/src/cmake/testing.cmake index 0ae2c200e..3ce6f2a3d 100644 --- a/src/cmake/testing.cmake +++ b/src/cmake/testing.cmake @@ -299,6 +299,7 @@ macro (osl_add_all_tests) oslc-err-struct-dup oslc-err-struct-print oslc-err-type-as-variable oslc-err-unknown-ctr + oslc-literalfold oslc-pragma-warnerr oslc-warn-commainit oslc-variadic-macro diff --git a/src/liboslcomp/ast.cpp b/src/liboslcomp/ast.cpp index 0ae74db48..74873e1c1 100644 --- a/src/liboslcomp/ast.cpp +++ b/src/liboslcomp/ast.cpp @@ -1187,6 +1187,69 @@ ASTbinary_expression::ASTbinary_expression(OSLCompilerImpl* comp, Operator op, +ASTNode* +ASTbinary_expression::make(OSLCompilerImpl* comp, Operator op, ASTNode* left, + ASTNode* right) +{ + // If the left and right are both literal constants, fold the expression + if (left->nodetype() == literal_node && right->nodetype() == literal_node) { + ASTNode* cf = nullptr; // constant-folded result + if (left->typespec().is_int() && right->typespec().is_int()) { + int lv = dynamic_cast(left)->intval(); + int rv = dynamic_cast(right)->intval(); + switch (op) { + case Mul: cf = new ASTliteral(comp, lv * rv); break; + case Div: cf = new ASTliteral(comp, rv ? lv / rv : 0); break; + case Add: cf = new ASTliteral(comp, lv + rv); break; + case Sub: cf = new ASTliteral(comp, lv - rv); break; + case Mod: cf = new ASTliteral(comp, rv ? lv % rv : 0); break; + case Equal: cf = new ASTliteral(comp, lv == rv ? 1 : 0); break; + case NotEqual: cf = new ASTliteral(comp, lv != rv ? 1 : 0); break; + case Greater: cf = new ASTliteral(comp, lv > rv ? 1 : 0); break; + case Less: cf = new ASTliteral(comp, lv < rv ? 1 : 0); break; + case GreaterEqual: + cf = new ASTliteral(comp, lv >= rv ? 1 : 0); + break; + case LessEqual: cf = new ASTliteral(comp, lv <= rv ? 1 : 0); break; + case BitAnd: cf = new ASTliteral(comp, lv & rv); break; + case BitOr: cf = new ASTliteral(comp, lv | rv); break; + case Xor: cf = new ASTliteral(comp, lv ^ rv); break; + case ShiftLeft: cf = new ASTliteral(comp, lv << rv); break; + case ShiftRight: cf = new ASTliteral(comp, lv >> rv); break; + default: break; + } + } else if (left->typespec().is_float() + && right->typespec().is_float()) { + float lv = dynamic_cast(left)->floatval(); + float rv = dynamic_cast(right)->floatval(); + switch (op) { + case Mul: cf = new ASTliteral(comp, lv * rv); break; + case Div: cf = new ASTliteral(comp, rv ? lv / rv : 0.0f); break; + case Add: cf = new ASTliteral(comp, lv + rv); break; + case Sub: cf = new ASTliteral(comp, lv - rv); break; + case Equal: cf = new ASTliteral(comp, lv == rv ? 1 : 0); break; + case NotEqual: cf = new ASTliteral(comp, lv != rv ? 1 : 0); break; + case Greater: cf = new ASTliteral(comp, lv > rv ? 1 : 0); break; + case Less: cf = new ASTliteral(comp, lv < rv ? 1 : 0); break; + case GreaterEqual: + cf = new ASTliteral(comp, lv >= rv ? 1 : 0); + break; + case LessEqual: cf = new ASTliteral(comp, lv <= rv ? 1 : 0); break; + default: break; + } + } + if (cf) { + delete left; + delete right; + return cf; + } + } + + return new ASTbinary_expression(comp, op, left, right); +} + + + const char* ASTbinary_expression::childname(size_t i) const { diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index db9bd14e5..f4bd2eeec 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -832,6 +832,10 @@ class ASTbinary_expression final : public ASTNode { ASTbinary_expression(OSLCompilerImpl* comp, Operator op, ASTNode* left, ASTNode* right); + // Special consructor wrapper that can collapse ops between literals + static ASTNode* make(OSLCompilerImpl* comp, Operator op, ASTNode* left, + ASTNode* right); + const char* nodetypename() const { return "binary_expression"; } const char* childname(size_t i) const; const char* opname() const; diff --git a/src/liboslcomp/oslgram.y b/src/liboslcomp/oslgram.y index b65818193..b4f3ad2e1 100644 --- a/src/liboslcomp/oslgram.y +++ b/src/liboslcomp/oslgram.y @@ -858,109 +858,109 @@ variable_ref binary_expression : expression OR_OP expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Or, $1, $3); $$->sourceline (@2.first_line); } | expression AND_OP expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::And, $1, $3); $$->sourceline (@2.first_line); } | expression '|' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::BitOr, $1, $3); $$->sourceline (@2.first_line); } | expression '^' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Xor, $1, $3); $$->sourceline (@2.first_line); } | expression '&' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::BitAnd, $1, $3); $$->sourceline (@2.first_line); } | expression EQ_OP expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Equal, $1, $3); $$->sourceline (@2.first_line); } | expression NE_OP expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::NotEqual, $1, $3); $$->sourceline (@2.first_line); } | expression '>' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Greater, $1, $3); $$->sourceline (@2.first_line); } | expression GE_OP expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::GreaterEqual, $1, $3); $$->sourceline (@2.first_line); } | expression '<' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Less, $1, $3); $$->sourceline (@2.first_line); } | expression LE_OP expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::LessEqual, $1, $3); $$->sourceline (@2.first_line); } | expression SHL_OP expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::ShiftLeft, $1, $3); $$->sourceline (@2.first_line); } | expression SHR_OP expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::ShiftRight, $1, $3); $$->sourceline (@2.first_line); } | expression '+' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Add, $1, $3); $$->sourceline (@2.first_line); } | expression '-' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Sub, $1, $3); $$->sourceline (@2.first_line); } | expression '*' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Mul, $1, $3); $$->sourceline (@2.first_line); } | expression '/' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Div, $1, $3); $$->sourceline (@2.first_line); } | expression '%' expression { - $$ = new ASTbinary_expression (oslcompiler, + $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Mod, $1, $3); $$->sourceline (@2.first_line); } diff --git a/testsuite/oslc-literalfold/ref/out.txt b/testsuite/oslc-literalfold/ref/out.txt new file mode 100644 index 000000000..9397357ff --- /dev/null +++ b/testsuite/oslc-literalfold/ref/out.txt @@ -0,0 +1,28 @@ +Compiled test.osl -> test.oso +int add_i = 8 +int sub_i = 2 +int mul_i = 15 +int div_i = 1 +int mod_i = 2 +int eq_i = 0 +int ne_i = 1 +int gt_i = 1 +int ge_i = 1 +int lt_i = 0 +int le_i = 0 +int and_i = 1 +int or_i = 7 +int xor_i = 6 +int shl_i = 40 +int shr_i = 0 +float add_f = 8.000000 +float sub_f = 2.000000 +float mul_f = 15.000000 +float div_f = 1.666667 +float eq_f = 0 +float ne_f = 1 +float gt_f = 1 +float ge_f = 1 +float lt_f = 0 +float le_f = 0 + diff --git a/testsuite/oslc-literalfold/run.py b/testsuite/oslc-literalfold/run.py new file mode 100755 index 000000000..6836d5554 --- /dev/null +++ b/testsuite/oslc-literalfold/run.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +# Copyright Contributors to the Open Shading Language project. +# SPDX-License-Identifier: BSD-3-Clause +# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +command = testshade("test") diff --git a/testsuite/oslc-literalfold/test.osl b/testsuite/oslc-literalfold/test.osl new file mode 100644 index 000000000..0f1b09e36 --- /dev/null +++ b/testsuite/oslc-literalfold/test.osl @@ -0,0 +1,69 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +// Test oslc-side constant folding of simple binary operators on literal +// values + +#define three 3 +#define five 5 + + +shader test( + int add_i = five + three, + int sub_i = five - three, + int mul_i = five * three, + int div_i = five / three, + int mod_i = five % three, + int eq_i = five == three, + int ne_i = five != three, + int gt_i = five > three, + int ge_i = five >= three, + int lt_i = five < three, + int le_i = five <= three, + int and_i = five & three, + int or_i = five | three, + int xor_i = five ^ three, + int shl_i = five << three, + int shr_i = five >> three, + + float add_f = float(five) + float(three), + float sub_f = float(five) - float(three), + float mul_f = float(five) * float(three), + float div_f = float(five) / float(three), + int eq_f = float(five) == float(three), + int ne_f = float(five) != float(three), + int gt_f = float(five) > float(three), + int ge_f = float(five) >= float(three), + int lt_f = float(five) < float(three), + int le_f = float(five) <= float(three), +) +{ + printf("int add_i = %d\n", add_i); + printf("int sub_i = %d\n", sub_i); + printf("int mul_i = %d\n", mul_i); + printf("int div_i = %d\n", div_i); + printf("int mod_i = %d\n", mod_i); + printf("int eq_i = %d\n", eq_i); + printf("int ne_i = %d\n", ne_i); + printf("int gt_i = %d\n", gt_i); + printf("int ge_i = %d\n", ge_i); + printf("int lt_i = %d\n", lt_i); + printf("int le_i = %d\n", le_i); + printf("int and_i = %d\n", and_i); + printf("int or_i = %d\n", or_i); + printf("int xor_i = %d\n", xor_i); + printf("int shl_i = %d\n", shl_i); + printf("int shr_i = %d\n", shr_i); + + printf("float add_f = %f\n", add_f); + printf("float sub_f = %f\n", sub_f); + printf("float mul_f = %f\n", mul_f); + printf("float div_f = %f\n", div_f); + printf("float eq_f = %d\n", eq_f); + printf("float ne_f = %d\n", ne_f); + printf("float gt_f = %d\n", gt_f); + printf("float ge_f = %d\n", ge_f); + printf("float lt_f = %d\n", lt_f); + printf("float le_f = %d\n", le_f); +} From 977f2898b10b9688c4bd65260884e1e477d29806 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Sun, 20 Aug 2023 21:36:59 -0700 Subject: [PATCH] fix: Recent journaling changes break with some fmtlib versions Certain fmt library versions don't automatically know how to format atomics. Explicitly load them to turn into regular ints to avoid new build errors introduced by the recent journaling changes. Signed-off-by: Larry Gritz --- .github/workflows/ci.yml | 2 +- src/liboslcomp/ast.cpp | 2 +- src/liboslexec/journal.cpp | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/liboslcomp/ast.cpp b/src/liboslcomp/ast.cpp index 74873e1c1..a8e5d75e4 100644 --- a/src/liboslcomp/ast.cpp +++ b/src/liboslcomp/ast.cpp @@ -46,7 +46,7 @@ ScopeExit print_node_counts([]() { for (int i = 0; i < ASTNode::_last_node; ++i) if (node_counts[i] > 0) Strutil::print("ASTNode type {:2}: {:5} (peak {:5})\n", i, - node_counts[i], node_counts_peak[i]); + node_counts[i].load(), node_counts_peak[i].load()); }); } // namespace #endif