michael@0: /* michael@0: * Copyright (C) 2012 Apple Inc. All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY michael@0: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE michael@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR michael@0: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE, INC. OR michael@0: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, michael@0: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, michael@0: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR michael@0: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY michael@0: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: michael@0: #include "third_party/compiler/ArrayBoundsClamper.h" michael@0: michael@0: // The built-in 'clamp' instruction only accepts floats and returns a float. I michael@0: // iterated a few times with our driver team who examined the output from our michael@0: // compiler - they said the multiple casts generates more code than a single michael@0: // function call. An inline ternary operator might have been better, but since michael@0: // the index value might be an expression itself, we'd have to make temporary michael@0: // variables to avoid evaluating the expression multiple times. And making michael@0: // temporary variables was difficult because ANGLE would then need to make more michael@0: // brutal changes to the expression tree. michael@0: michael@0: const char* kIntClampBegin = "// BEGIN: Generated code for array bounds clamping\n\n"; michael@0: const char* kIntClampEnd = "// END: Generated code for array bounds clamping\n\n"; michael@0: const char* kIntClampDefinition = "int webgl_int_clamp(int value, int minValue, int maxValue) { return ((value < minValue) ? minValue : ((value > maxValue) ? maxValue : value)); }\n\n"; michael@0: michael@0: namespace { michael@0: michael@0: class ArrayBoundsClamperMarker : public TIntermTraverser { michael@0: public: michael@0: ArrayBoundsClamperMarker() michael@0: : mNeedsClamp(false) michael@0: { michael@0: } michael@0: michael@0: virtual bool visitBinary(Visit visit, TIntermBinary* node) michael@0: { michael@0: if (node->getOp() == EOpIndexIndirect) michael@0: { michael@0: TIntermTyped* left = node->getLeft(); michael@0: if (left->isArray() || left->isVector() || left->isMatrix()) michael@0: { michael@0: node->setAddIndexClamp(); michael@0: mNeedsClamp = true; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool GetNeedsClamp() { return mNeedsClamp; } michael@0: michael@0: private: michael@0: bool mNeedsClamp; michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: ArrayBoundsClamper::ArrayBoundsClamper() michael@0: : mClampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC) michael@0: , mArrayBoundsClampDefinitionNeeded(false) michael@0: { michael@0: } michael@0: michael@0: void ArrayBoundsClamper::SetClampingStrategy(ShArrayIndexClampingStrategy clampingStrategy) michael@0: { michael@0: ASSERT(clampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC || michael@0: clampingStrategy == SH_CLAMP_WITH_USER_DEFINED_INT_CLAMP_FUNCTION); michael@0: michael@0: mClampingStrategy = clampingStrategy; michael@0: } michael@0: michael@0: void ArrayBoundsClamper::MarkIndirectArrayBoundsForClamping(TIntermNode* root) michael@0: { michael@0: ASSERT(root); michael@0: michael@0: ArrayBoundsClamperMarker clamper; michael@0: root->traverse(&clamper); michael@0: if (clamper.GetNeedsClamp()) michael@0: { michael@0: SetArrayBoundsClampDefinitionNeeded(); michael@0: } michael@0: } michael@0: michael@0: void ArrayBoundsClamper::OutputClampingFunctionDefinition(TInfoSinkBase& out) const michael@0: { michael@0: if (!mArrayBoundsClampDefinitionNeeded) michael@0: { michael@0: return; michael@0: } michael@0: if (mClampingStrategy != SH_CLAMP_WITH_USER_DEFINED_INT_CLAMP_FUNCTION) michael@0: { michael@0: return; michael@0: } michael@0: out << kIntClampBegin << kIntClampDefinition << kIntClampEnd; michael@0: }