//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RemoveAtomicCounterBuiltins: Remove atomic counter builtins.
//

#include "compiler/translator/tree_ops/RemoveAtomicCounterBuiltins.h"

#include "compiler/translator/Compiler.h"
#include "compiler/translator/tree_util/IntermTraverse.h"

namespace sh
{
namespace
{

bool IsAtomicCounterDecl(const TIntermDeclaration *node)
{
    const TIntermSequence &sequence = *(node->getSequence());
    TIntermTyped *variable          = sequence.front()->getAsTyped();
    const TType &type               = variable->getType();
    return type.getQualifier() == EvqUniform && type.isAtomicCounter();
}

// Traverser that removes all GLSL built-ins that use AtomicCounters
// Only called when the builtins are in use, but no atomic counters have been declared
class RemoveAtomicCounterBuiltinsTraverser : public TIntermTraverser
{
  public:
    RemoveAtomicCounterBuiltinsTraverser() : TIntermTraverser(true, false, false) {}

    bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
    {
        ASSERT(visit == PreVisit);

        // Active atomic counters should have been removed by RewriteAtomicCounters, and this
        // traversal should not have been invoked
        ASSERT(!IsAtomicCounterDecl(node));
        return false;
    }

    bool visitAggregate(Visit visit, TIntermAggregate *node) override
    {
        if (node->getOp() == EOpMemoryBarrierAtomicCounter)
        {
            // Vulkan does not support atomic counters, so if this builtin finds its way here,
            // we need to remove it.
            TIntermSequence emptySequence;
            mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, emptySequence);
            return true;
        }

        // We shouldn't see any other builtins because they cannot be present without an active
        // atomic counter, and should have been removed by RewriteAtomicCounters. If this fires,
        // this traversal should not have been called.
        ASSERT(!(node->getOp() == EOpCallBuiltInFunction &&
                 node->getFunction()->isAtomicCounterFunction()));

        return false;
    }
};

}  // anonymous namespace

bool RemoveAtomicCounterBuiltins(TCompiler *compiler, TIntermBlock *root)
{
    RemoveAtomicCounterBuiltinsTraverser traverser;
    root->traverse(&traverser);
    return traverser.updateTree(compiler, root);
}
}  // namespace sh
