mirror of
https://github.com/ZDoom/ZDRay.git
synced 2024-11-25 21:31:32 +00:00
371 lines
12 KiB
C++
371 lines
12 KiB
C++
//
|
|
// Copyright (C) 2017 LunarG, Inc.
|
|
// Copyright (C) 2018 Google, Inc.
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions
|
|
// are met:
|
|
//
|
|
// Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
//
|
|
// Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following
|
|
// disclaimer in the documentation and/or other materials provided
|
|
// with the distribution.
|
|
//
|
|
// Neither the name of Google, Inc., nor the names of its
|
|
// contributors may be used to endorse or promote products derived
|
|
// from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
|
|
#ifndef GLSLANG_WEB
|
|
|
|
#include "attribute.h"
|
|
#include "../Include/intermediate.h"
|
|
#include "ParseHelper.h"
|
|
|
|
namespace glslang {
|
|
|
|
// extract integers out of attribute arguments stored in attribute aggregate
|
|
bool TAttributeArgs::getInt(int& value, int argNum) const
|
|
{
|
|
const TConstUnion* intConst = getConstUnion(EbtInt, argNum);
|
|
|
|
if (intConst == nullptr)
|
|
return false;
|
|
|
|
value = intConst->getIConst();
|
|
return true;
|
|
}
|
|
|
|
|
|
// extract strings out of attribute arguments stored in attribute aggregate.
|
|
// convert to lower case if converToLower is true (for case-insensitive compare convenience)
|
|
bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const
|
|
{
|
|
const TConstUnion* stringConst = getConstUnion(EbtString, argNum);
|
|
|
|
if (stringConst == nullptr)
|
|
return false;
|
|
|
|
value = *stringConst->getSConst();
|
|
|
|
// Convenience.
|
|
if (convertToLower)
|
|
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
|
|
|
|
return true;
|
|
}
|
|
|
|
// How many arguments were supplied?
|
|
int TAttributeArgs::size() const
|
|
{
|
|
return args == nullptr ? 0 : (int)args->getSequence().size();
|
|
}
|
|
|
|
// Helper to get attribute const union. Returns nullptr on failure.
|
|
const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const
|
|
{
|
|
if (args == nullptr)
|
|
return nullptr;
|
|
|
|
if (argNum >= (int)args->getSequence().size())
|
|
return nullptr;
|
|
|
|
if (args->getSequence()[argNum]->getAsConstantUnion() == nullptr)
|
|
return nullptr;
|
|
|
|
const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0];
|
|
if (constVal == nullptr || constVal->getType() != basicType)
|
|
return nullptr;
|
|
|
|
return constVal;
|
|
}
|
|
|
|
// Implementation of TParseContext parts of attributes
|
|
TAttributeType TParseContext::attributeFromName(const TString& name) const
|
|
{
|
|
if (name == "branch" || name == "dont_flatten")
|
|
return EatBranch;
|
|
else if (name == "flatten")
|
|
return EatFlatten;
|
|
else if (name == "unroll")
|
|
return EatUnroll;
|
|
else if (name == "loop" || name == "dont_unroll")
|
|
return EatLoop;
|
|
else if (name == "dependency_infinite")
|
|
return EatDependencyInfinite;
|
|
else if (name == "dependency_length")
|
|
return EatDependencyLength;
|
|
else if (name == "min_iterations")
|
|
return EatMinIterations;
|
|
else if (name == "max_iterations")
|
|
return EatMaxIterations;
|
|
else if (name == "iteration_multiple")
|
|
return EatIterationMultiple;
|
|
else if (name == "peel_count")
|
|
return EatPeelCount;
|
|
else if (name == "partial_count")
|
|
return EatPartialCount;
|
|
else if (name == "subgroup_uniform_control_flow")
|
|
return EatSubgroupUniformControlFlow;
|
|
else
|
|
return EatNone;
|
|
}
|
|
|
|
// Make an initial leaf for the grammar from a no-argument attribute
|
|
TAttributes* TParseContext::makeAttributes(const TString& identifier) const
|
|
{
|
|
TAttributes *attributes = nullptr;
|
|
attributes = NewPoolObject(attributes);
|
|
TAttributeArgs args = { attributeFromName(identifier), nullptr };
|
|
attributes->push_back(args);
|
|
return attributes;
|
|
}
|
|
|
|
// Make an initial leaf for the grammar from a one-argument attribute
|
|
TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const
|
|
{
|
|
TAttributes *attributes = nullptr;
|
|
attributes = NewPoolObject(attributes);
|
|
|
|
// for now, node is always a simple single expression, but other code expects
|
|
// a list, so make it so
|
|
TIntermAggregate* agg = intermediate.makeAggregate(node);
|
|
TAttributeArgs args = { attributeFromName(identifier), agg };
|
|
attributes->push_back(args);
|
|
return attributes;
|
|
}
|
|
|
|
// Merge two sets of attributes into a single set.
|
|
// The second argument is destructively consumed.
|
|
TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const
|
|
{
|
|
attr1->splice(attr1->end(), *attr2);
|
|
return attr1;
|
|
}
|
|
|
|
//
|
|
// Selection attributes
|
|
//
|
|
void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node)
|
|
{
|
|
TIntermSelection* selection = node->getAsSelectionNode();
|
|
if (selection == nullptr)
|
|
return;
|
|
|
|
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
|
|
if (it->size() > 0) {
|
|
warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
|
|
continue;
|
|
}
|
|
|
|
switch (it->name) {
|
|
case EatFlatten:
|
|
selection->setFlatten();
|
|
break;
|
|
case EatBranch:
|
|
selection->setDontFlatten();
|
|
break;
|
|
default:
|
|
warn(node->getLoc(), "attribute does not apply to a selection", "", "");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Switch attributes
|
|
//
|
|
void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node)
|
|
{
|
|
TIntermSwitch* selection = node->getAsSwitchNode();
|
|
if (selection == nullptr)
|
|
return;
|
|
|
|
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
|
|
if (it->size() > 0) {
|
|
warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
|
|
continue;
|
|
}
|
|
|
|
switch (it->name) {
|
|
case EatFlatten:
|
|
selection->setFlatten();
|
|
break;
|
|
case EatBranch:
|
|
selection->setDontFlatten();
|
|
break;
|
|
default:
|
|
warn(node->getLoc(), "attribute does not apply to a switch", "", "");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop attributes
|
|
//
|
|
void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node)
|
|
{
|
|
TIntermLoop* loop = node->getAsLoopNode();
|
|
if (loop == nullptr) {
|
|
// the actual loop might be part of a sequence
|
|
TIntermAggregate* agg = node->getAsAggregate();
|
|
if (agg == nullptr)
|
|
return;
|
|
for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) {
|
|
loop = (*it)->getAsLoopNode();
|
|
if (loop != nullptr)
|
|
break;
|
|
}
|
|
if (loop == nullptr)
|
|
return;
|
|
}
|
|
|
|
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
|
|
|
|
const auto noArgument = [&](const char* feature) {
|
|
if (it->size() > 0) {
|
|
warn(node->getLoc(), "expected no arguments", feature, "");
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const auto positiveSignedArgument = [&](const char* feature, int& value) {
|
|
if (it->size() == 1 && it->getInt(value)) {
|
|
if (value <= 0) {
|
|
error(node->getLoc(), "must be positive", feature, "");
|
|
return false;
|
|
}
|
|
} else {
|
|
warn(node->getLoc(), "expected a single integer argument", feature, "");
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) {
|
|
int value;
|
|
if (!(it->size() == 1 && it->getInt(value))) {
|
|
warn(node->getLoc(), "expected a single integer argument", feature, "");
|
|
return false;
|
|
}
|
|
uiValue = (unsigned int)value;
|
|
return true;
|
|
};
|
|
|
|
const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) {
|
|
int value;
|
|
if (it->size() == 1 && it->getInt(value)) {
|
|
if (value == 0) {
|
|
error(node->getLoc(), "must be greater than or equal to 1", feature, "");
|
|
return false;
|
|
}
|
|
} else {
|
|
warn(node->getLoc(), "expected a single integer argument", feature, "");
|
|
return false;
|
|
}
|
|
uiValue = (unsigned int)value;
|
|
return true;
|
|
};
|
|
|
|
const auto spirv14 = [&](const char* feature) {
|
|
if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4)
|
|
warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, "");
|
|
};
|
|
|
|
int value = 0;
|
|
unsigned uiValue = 0;
|
|
switch (it->name) {
|
|
case EatUnroll:
|
|
if (noArgument("unroll"))
|
|
loop->setUnroll();
|
|
break;
|
|
case EatLoop:
|
|
if (noArgument("dont_unroll"))
|
|
loop->setDontUnroll();
|
|
break;
|
|
case EatDependencyInfinite:
|
|
if (noArgument("dependency_infinite"))
|
|
loop->setLoopDependency(TIntermLoop::dependencyInfinite);
|
|
break;
|
|
case EatDependencyLength:
|
|
if (positiveSignedArgument("dependency_length", value))
|
|
loop->setLoopDependency(value);
|
|
break;
|
|
case EatMinIterations:
|
|
spirv14("min_iterations");
|
|
if (unsignedArgument("min_iterations", uiValue))
|
|
loop->setMinIterations(uiValue);
|
|
break;
|
|
case EatMaxIterations:
|
|
spirv14("max_iterations");
|
|
if (unsignedArgument("max_iterations", uiValue))
|
|
loop->setMaxIterations(uiValue);
|
|
break;
|
|
case EatIterationMultiple:
|
|
spirv14("iteration_multiple");
|
|
if (positiveUnsignedArgument("iteration_multiple", uiValue))
|
|
loop->setIterationMultiple(uiValue);
|
|
break;
|
|
case EatPeelCount:
|
|
spirv14("peel_count");
|
|
if (unsignedArgument("peel_count", uiValue))
|
|
loop->setPeelCount(uiValue);
|
|
break;
|
|
case EatPartialCount:
|
|
spirv14("partial_count");
|
|
if (unsignedArgument("partial_count", uiValue))
|
|
loop->setPartialCount(uiValue);
|
|
break;
|
|
default:
|
|
warn(node->getLoc(), "attribute does not apply to a loop", "", "");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Function attributes
|
|
//
|
|
void TParseContext::handleFunctionAttributes(const TSourceLoc& loc, const TAttributes& attributes, TFunction* function)
|
|
{
|
|
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
|
|
if (it->size() > 0) {
|
|
warn(loc, "attribute with arguments not recognized, skipping", "", "");
|
|
continue;
|
|
}
|
|
|
|
switch (it->name) {
|
|
case EatSubgroupUniformControlFlow:
|
|
intermediate.setSubgroupUniformControlFlow();
|
|
break;
|
|
default:
|
|
warn(loc, "attribute does not apply to a function", "", "");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // end namespace glslang
|
|
|
|
#endif // GLSLANG_WEB
|