<commit_before>//
// Copyright 2018 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.
//
// MulithreadingTest.cpp : Tests of multithreaded rendering

#include "test_utils/ANGLETest.h"

#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"

#include <atomic>
#include <mutex>
#include <thread>

namespace angle
{

class MultithreadingTest : public ANGLETest
{
  protected:
    MultithreadingTest()
    {
        setWindowWidth(128);
        setWindowHeight(128);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
    }

    void runMultithreadedGLTest(
        std::function<void(EGLSurface surface, size_t threadIndex)> testBody,
        size_t threadCount)
    {
        std::mutex mutex;

        EGLWindow *window = getEGLWindow();
        EGLDisplay dpy    = window->getDisplay();
        EGLConfig config  = window->getConfig();

        constexpr EGLint kPBufferSize = 256;

        std::vector<std::thread> threads(threadCount);
        for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
        {
            threads[threadIdx] = std::thread([&, threadIdx]() {
                EGLSurface surface = EGL_NO_SURFACE;
                EGLContext ctx     = EGL_NO_CONTEXT;

                {
                    std::lock_guard<decltype(mutex)> lock(mutex);

                    // Initialize the pbuffer and context
                    EGLint pbufferAttributes[] = {
                        EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
                    };
                    surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
                    EXPECT_EGL_SUCCESS();

                    ctx = window->createContext(EGL_NO_CONTEXT);
                    EXPECT_NE(EGL_NO_CONTEXT, ctx);

                    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
                    EXPECT_EGL_SUCCESS();
                }

                testBody(surface, threadIdx);

                {
                    std::lock_guard<decltype(mutex)> lock(mutex);

                    // Clean up
                    EXPECT_EGL_TRUE(
                        eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
                    EXPECT_EGL_SUCCESS();

                    eglDestroySurface(dpy, surface);
                    eglDestroyContext(dpy, ctx);
                }
            });
        }

        for (std::thread &thread : threads)
        {
            thread.join();
        }
    }
};

// Test that it's possible to make one context current on different threads
TEST_P(MultithreadingTest, MakeCurrentSingleContext)
{
    ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());

    std::mutex mutex;

    EGLWindow *window  = getEGLWindow();
    EGLDisplay dpy     = window->getDisplay();
    EGLContext ctx     = window->getContext();
    EGLSurface surface = window->getSurface();

    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
    EXPECT_EGL_SUCCESS();

    constexpr size_t kThreadCount = 16;
    std::array<std::thread, kThreadCount> threads;
    for (std::thread &thread : threads)
    {
        thread = std::thread([&]() {
            std::lock_guard<decltype(mutex)> lock(mutex);

            EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
            EXPECT_EGL_SUCCESS();

            EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
            EXPECT_EGL_SUCCESS();

            EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
            EXPECT_EGL_SUCCESS();
        });
    }

    for (std::thread &thread : threads)
    {
        thread.join();
    }

    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
    EXPECT_EGL_SUCCESS();
}

// Test that multiple threads can clear and readback pixels successfully at the same time
TEST_P(MultithreadingTest, MultiContextClear)
{
    ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());

    auto testBody = [](EGLSurface surface, size_t thread) {
        constexpr size_t kIterationsPerThread = 32;
        for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
        {
            // Base the clear color on the thread and iteration indexes so every clear color is
            // unique
            const GLColor color(static_cast<GLubyte>(thread % 255),
                                static_cast<GLubyte>(iteration % 255), 0, 255);
            const angle::Vector4 floatColor = color.toNormalizedVector();

            glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]);
            EXPECT_GL_NO_ERROR();

            glClear(GL_COLOR_BUFFER_BIT);
            EXPECT_GL_NO_ERROR();

            EXPECT_PIXEL_COLOR_EQ(0, 0, color);
        }
    };
    runMultithreadedGLTest(testBody, 72);
}

// Test that multiple threads can draw and readback pixels successfully at the same time
TEST_P(MultithreadingTest, MultiContextDraw)
{
    ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());

    auto testBody = [](EGLSurface surface, size_t thread) {
        constexpr size_t kIterationsPerThread = 32;
        constexpr size_t kDrawsPerIteration   = 500;

        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(program);

        GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());

        auto quadVertices = GetQuadVertices();

        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);

        GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
        glEnableVertexAttribArray(positionLocation);
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);

        for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
        {
            // Base the clear color on the thread and iteration indexes so every clear color is
            // unique
            const GLColor color(static_cast<GLubyte>(thread % 255),
                                static_cast<GLubyte>(iteration % 255), 0, 255);
            const angle::Vector4 floatColor = color.toNormalizedVector();
            glUniform4fv(colorLocation, 1, floatColor.data());

            for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
            {
                glDrawArrays(GL_TRIANGLES, 0, 6);
            }

            EXPECT_PIXEL_COLOR_EQ(0, 0, color);
        }
    };
    runMultithreadedGLTest(testBody, 4);
}

TEST_P(MultithreadingTest, MultiCreateContext)
{
    // Supported by CGL, GLX, and WGL (https://anglebug.com/4725)
    ANGLE_SKIP_TEST_IF(!(IsWindows() || IsLinux() || IsOSX()));

    EGLWindow *window  = getEGLWindow();
    EGLDisplay dpy     = window->getDisplay();
    EGLContext ctx     = window->getContext();
    EGLSurface surface = window->getSurface();

    // Un-makeCurrent the test window's context
    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
    EXPECT_EGL_SUCCESS();

    constexpr size_t kThreadCount = 16;
    std::atomic<uint32_t> barrier(0);
    std::vector<std::thread> threads(kThreadCount);
    std::vector<EGLContext> contexts(kThreadCount);
    for (size_t threadIdx = 0; threadIdx < kThreadCount; threadIdx++)
    {
        threads[threadIdx] = std::thread([&, threadIdx]() {
            contexts[threadIdx] = EGL_NO_CONTEXT;
            {
                contexts[threadIdx] = window->createContext(EGL_NO_CONTEXT);
                EXPECT_NE(EGL_NO_CONTEXT, contexts[threadIdx]);

                barrier++;
            }

            while (barrier < kThreadCount)
            {
            }

            {
                EXPECT_TRUE(eglDestroyContext(dpy, contexts[threadIdx]));
            }
        });
    }

    for (std::thread &thread : threads)
    {
        thread.join();
    }

    // Re-make current the test window's context for teardown.
    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
    EXPECT_EGL_SUCCESS();
}

// TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads

ANGLE_INSTANTIATE_TEST(MultithreadingTest,
                       WithNoVirtualContexts(ES2_OPENGL()),
                       WithNoVirtualContexts(ES3_OPENGL()),
                       WithNoVirtualContexts(ES2_OPENGLES()),
                       WithNoVirtualContexts(ES3_OPENGLES()));

}  // namespace angle
<commit_msg>Skip MultithreadingTest.MultiCreateContext on Ozone<commit_after>//
// Copyright 2018 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.
//
// MulithreadingTest.cpp : Tests of multithreaded rendering

#include "test_utils/ANGLETest.h"

#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"

#include <atomic>
#include <mutex>
#include <thread>

namespace angle
{

class MultithreadingTest : public ANGLETest
{
  protected:
    MultithreadingTest()
    {
        setWindowWidth(128);
        setWindowHeight(128);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
    }

    void runMultithreadedGLTest(
        std::function<void(EGLSurface surface, size_t threadIndex)> testBody,
        size_t threadCount)
    {
        std::mutex mutex;

        EGLWindow *window = getEGLWindow();
        EGLDisplay dpy    = window->getDisplay();
        EGLConfig config  = window->getConfig();

        constexpr EGLint kPBufferSize = 256;

        std::vector<std::thread> threads(threadCount);
        for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
        {
            threads[threadIdx] = std::thread([&, threadIdx]() {
                EGLSurface surface = EGL_NO_SURFACE;
                EGLContext ctx     = EGL_NO_CONTEXT;

                {
                    std::lock_guard<decltype(mutex)> lock(mutex);

                    // Initialize the pbuffer and context
                    EGLint pbufferAttributes[] = {
                        EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
                    };
                    surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
                    EXPECT_EGL_SUCCESS();

                    ctx = window->createContext(EGL_NO_CONTEXT);
                    EXPECT_NE(EGL_NO_CONTEXT, ctx);

                    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
                    EXPECT_EGL_SUCCESS();
                }

                testBody(surface, threadIdx);

                {
                    std::lock_guard<decltype(mutex)> lock(mutex);

                    // Clean up
                    EXPECT_EGL_TRUE(
                        eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
                    EXPECT_EGL_SUCCESS();

                    eglDestroySurface(dpy, surface);
                    eglDestroyContext(dpy, ctx);
                }
            });
        }

        for (std::thread &thread : threads)
        {
            thread.join();
        }
    }
};

// Test that it's possible to make one context current on different threads
TEST_P(MultithreadingTest, MakeCurrentSingleContext)
{
    ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());

    std::mutex mutex;

    EGLWindow *window  = getEGLWindow();
    EGLDisplay dpy     = window->getDisplay();
    EGLContext ctx     = window->getContext();
    EGLSurface surface = window->getSurface();

    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
    EXPECT_EGL_SUCCESS();

    constexpr size_t kThreadCount = 16;
    std::array<std::thread, kThreadCount> threads;
    for (std::thread &thread : threads)
    {
        thread = std::thread([&]() {
            std::lock_guard<decltype(mutex)> lock(mutex);

            EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
            EXPECT_EGL_SUCCESS();

            EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
            EXPECT_EGL_SUCCESS();

            EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
            EXPECT_EGL_SUCCESS();
        });
    }

    for (std::thread &thread : threads)
    {
        thread.join();
    }

    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
    EXPECT_EGL_SUCCESS();
}

// Test that multiple threads can clear and readback pixels successfully at the same time
TEST_P(MultithreadingTest, MultiContextClear)
{
    ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());

    auto testBody = [](EGLSurface surface, size_t thread) {
        constexpr size_t kIterationsPerThread = 32;
        for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
        {
            // Base the clear color on the thread and iteration indexes so every clear color is
            // unique
            const GLColor color(static_cast<GLubyte>(thread % 255),
                                static_cast<GLubyte>(iteration % 255), 0, 255);
            const angle::Vector4 floatColor = color.toNormalizedVector();

            glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]);
            EXPECT_GL_NO_ERROR();

            glClear(GL_COLOR_BUFFER_BIT);
            EXPECT_GL_NO_ERROR();

            EXPECT_PIXEL_COLOR_EQ(0, 0, color);
        }
    };
    runMultithreadedGLTest(testBody, 72);
}

// Test that multiple threads can draw and readback pixels successfully at the same time
TEST_P(MultithreadingTest, MultiContextDraw)
{
    ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());

    auto testBody = [](EGLSurface surface, size_t thread) {
        constexpr size_t kIterationsPerThread = 32;
        constexpr size_t kDrawsPerIteration   = 500;

        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(program);

        GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());

        auto quadVertices = GetQuadVertices();

        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);

        GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
        glEnableVertexAttribArray(positionLocation);
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);

        for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
        {
            // Base the clear color on the thread and iteration indexes so every clear color is
            // unique
            const GLColor color(static_cast<GLubyte>(thread % 255),
                                static_cast<GLubyte>(iteration % 255), 0, 255);
            const angle::Vector4 floatColor = color.toNormalizedVector();
            glUniform4fv(colorLocation, 1, floatColor.data());

            for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
            {
                glDrawArrays(GL_TRIANGLES, 0, 6);
            }

            EXPECT_PIXEL_COLOR_EQ(0, 0, color);
        }
    };
    runMultithreadedGLTest(testBody, 4);
}

TEST_P(MultithreadingTest, MultiCreateContext)
{
    // Supported by CGL, GLX, and WGL (https://anglebug.com/4725)
    // Not supported on Ozone (https://crbug.com/1103009)
    ANGLE_SKIP_TEST_IF(!(IsWindows() || IsLinux() || IsOSX()) || IsOzone());

    EGLWindow *window  = getEGLWindow();
    EGLDisplay dpy     = window->getDisplay();
    EGLContext ctx     = window->getContext();
    EGLSurface surface = window->getSurface();

    // Un-makeCurrent the test window's context
    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
    EXPECT_EGL_SUCCESS();

    constexpr size_t kThreadCount = 16;
    std::atomic<uint32_t> barrier(0);
    std::vector<std::thread> threads(kThreadCount);
    std::vector<EGLContext> contexts(kThreadCount);
    for (size_t threadIdx = 0; threadIdx < kThreadCount; threadIdx++)
    {
        threads[threadIdx] = std::thread([&, threadIdx]() {
            contexts[threadIdx] = EGL_NO_CONTEXT;
            {
                contexts[threadIdx] = window->createContext(EGL_NO_CONTEXT);
                EXPECT_NE(EGL_NO_CONTEXT, contexts[threadIdx]);

                barrier++;
            }

            while (barrier < kThreadCount)
            {
            }

            {
                EXPECT_TRUE(eglDestroyContext(dpy, contexts[threadIdx]));
            }
        });
    }

    for (std::thread &thread : threads)
    {
        thread.join();
    }

    // Re-make current the test window's context for teardown.
    EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
    EXPECT_EGL_SUCCESS();
}

// TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads

ANGLE_INSTANTIATE_TEST(MultithreadingTest,
                       WithNoVirtualContexts(ES2_OPENGL()),
                       WithNoVirtualContexts(ES3_OPENGL()),
                       WithNoVirtualContexts(ES2_OPENGLES()),
                       WithNoVirtualContexts(ES3_OPENGLES()));

}  // namespace angle
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Manasij Mukherjee  <manasij7479@gmail.com>
// author:  Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

#include "clang/Sema/Sema.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h" // for operator new[](unsigned long, ASTCtx..)
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/AutoloadCallback.h"
#include "cling/Interpreter/Transaction.h"
#include "DeclUnloader.h"

namespace {
  static const char annoTag[] = "$clingAutoload$";
  static const size_t lenAnnoTag = sizeof(annoTag) - 1;
}

using namespace clang;

namespace cling {

  void AutoloadCallback::report(clang::SourceLocation l, llvm::StringRef name,
                                llvm::StringRef header) {
    Sema& sema= m_Interpreter->getSema();

    unsigned id
      = sema.getDiagnostics().getCustomDiagID (DiagnosticsEngine::Level::Warning,
                                                 "Note: '%0' can be found in %1");
/*    unsigned idn //TODO: To be enabled after we have a way to get the full path
      = sema.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Level::Note,
                                                "Type : %0 , Full Path: %1")*/;

    if (header.startswith(llvm::StringRef(annoTag, lenAnnoTag)))
      sema.Diags.Report(l, id) << name << header.drop_front(lenAnnoTag);

  }

  bool AutoloadCallback::LookupObject (TagDecl *t) {
    if (m_ShowSuggestions && t->hasAttr<AnnotateAttr>())
      report(t->getLocation(),t->getNameAsString(),t->getAttr<AnnotateAttr>()->getAnnotation());
    return false;
  }

  class AutoloadingVisitor: public RecursiveASTVisitor<AutoloadingVisitor> {
  private:
    ///\brief Flag determining the visitor's actions. If true, register autoload
    /// entries, i.e. remember the connection between filename and the declaration
    /// that needs to be updated on #include of the filename.
    /// If false, react on an #include by adjusting the forward decls, e.g. by
    /// removing the default tremplate arguments (that will now be provided by
    /// the definition read from the include) and by removing enum declarations
    /// that would otherwise be duplicates.
    bool m_IsStoringState;
    AutoloadCallback::FwdDeclsMap* m_Map;
    clang::Preprocessor* m_PP;
    clang::Sema* m_Sema;
    const clang::FileEntry* m_PrevFE;
    std::string m_PrevFileName;
  private:
    bool IsAutoloadEntry(Decl *D) {
       AnnotateAttr* attr = D->getAttr<AnnotateAttr>();
       if (attr && !attr->isInherited()) {
         llvm::StringRef annotation = attr->getAnnotation();
         assert(!annotation.empty() && "Empty annotation!");
         if (annotation.startswith(llvm::StringRef(annoTag, lenAnnoTag))) {
            // autoload annotation.
            return true;
         }
       }
       return false;
    }

    void InsertIntoAutoloadingState (Decl* decl, llvm::StringRef annotation) {

      assert(!annotation.empty() && "Empty annotation!");
      if (!annotation.startswith(llvm::StringRef(annoTag, lenAnnoTag))) {
        // not an autoload annotation.
        return;
      }

      assert(m_PP);

      const FileEntry* FE = 0;
      SourceLocation fileNameLoc;
      bool isAngled = false;
      const DirectoryLookup* FromDir = 0;
      const FileEntry* FromFile = 0;
      const DirectoryLookup* CurDir = 0;
      llvm::StringRef FileName = annotation.drop_front(lenAnnoTag);
      if (FileName.equals(m_PrevFileName))
        FE = m_PrevFE;
      else {
        FE = m_PP->LookupFile(fileNameLoc, FileName, isAngled,
                              FromDir, FromFile, CurDir, /*SearchPath*/0,
                              /*RelativePath*/ 0, /*suggestedModule*/0,
                              /*SkipCache*/ false, /*OpenFile*/ false,
                              /*CacheFail*/ true);
        m_PrevFE = FE;
        m_PrevFileName = FileName;
      }

      if (FE) {
        auto& Vec = (*m_Map)[FE];
        Vec.push_back(decl);
      } else {
        llvm::errs()
          << "Error in cling::AutoloadingVisitor::InsertIntoAutoloadingState:\n"
          "   Missing FileEntry for " << FileName << "\n";
        if (NamedDecl* ND = dyn_cast<NamedDecl>(decl)) {
          llvm::errs() << "   requested to autoload type ";
          ND->getNameForDiagnostic(llvm::errs(),
                                   ND->getASTContext().getPrintingPolicy(),
                                   true /*qualified*/);
          llvm::errs() << "\n";
        }
      }
    }

  public:
    AutoloadingVisitor():
      m_IsStoringState(false), m_Map(0), m_PP(0), m_Sema(0), m_PrevFE(0) {}
    void RemoveDefaultArgsOf(Decl* D, Sema* S) {
      m_Sema = S;

      auto cursor = D->getMostRecentDecl();
      TraverseDecl(cursor);
      while (cursor != D && (cursor = cursor->getPreviousDecl())) {
        TraverseDecl(cursor);
      }
    }

    void TrackDefaultArgStateOf(Decl* D, AutoloadCallback::FwdDeclsMap& map,
                                Preprocessor& PP) {
      m_IsStoringState = true;
      m_Map = ↦
      m_PP = &PP;
      TraverseDecl(D);
      m_PP = 0;
      m_Map = 0;
      m_IsStoringState = false;
    }

    bool shouldVisitTemplateInstantiations() { return true; }

    bool VisitDecl(Decl* D) {
      if (!m_IsStoringState)
        return true;

      if (!D->hasAttr<AnnotateAttr>())
        return true;

      if (AnnotateAttr* attr = D->getAttr<AnnotateAttr>())
        if (!attr->isInherited())
           InsertIntoAutoloadingState(D, attr->getAnnotation());

      return true;
    }

    bool VisitCXXRecordDecl(CXXRecordDecl* D) {
      if (!D->hasAttr<AnnotateAttr>())
        return true;

      if (ClassTemplateDecl* TmplD = D->getDescribedClassTemplate())
        return VisitTemplateDecl(TmplD);
      return true;
    }

    bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl* D) {
      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitTemplateDecl(TemplateDecl* D) {
      if (D->getTemplatedDecl() &&
          !D->getTemplatedDecl()->hasAttr<AnnotateAttr>())
        return true;

      // If we have a definition we might be about to re-#include the
      // same header containing definition that was #included previously,
      // i.e. we might have multiple fwd decls for the same template.
      // DO NOT remove the defaults here; the definition needs to keep it.
      // (ROOT-7037)
      if (ClassTemplateDecl* CTD = dyn_cast<ClassTemplateDecl>(D))
        if (CXXRecordDecl* TemplatedD = CTD->getTemplatedDecl())
          if (TemplatedD->getDefinition())
            return true;

      for(auto P: D->getTemplateParameters()->asArray())
        TraverseDecl(P);
      return true;
    }

    bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl* D) {
      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl* D) {
      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitParmVarDecl(ParmVarDecl* D) {
      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArg())
        D->setDefaultArg(nullptr);
      return true;
    }

    bool VisitEnumDecl(EnumDecl* D) {
      if (m_IsStoringState)
        return true;

      // Now that we will read the full enum, unload the forward decl.
      if (IsAutoloadEntry(D))
        UnloadDecl(m_Sema, D);
      return true;
    }
  };

  void AutoloadCallback::InclusionDirective(clang::SourceLocation HashLoc,
                          const clang::Token &IncludeTok,
                          llvm::StringRef FileName,
                          bool IsAngled,
                          clang::CharSourceRange FilenameRange,
                          const clang::FileEntry *File,
                          llvm::StringRef SearchPath,
                          llvm::StringRef RelativePath,
                          const clang::Module *Imported) {
    // If File is 0 this means that the #included file doesn't exist.
    if (!File)
      return;

    auto found = m_Map.find(File);
    if (found == m_Map.end())
     return; // nothing to do, file not referred in any annotation

    AutoloadingVisitor defaultArgsCleaner;
    for (auto D : found->second) {
      defaultArgsCleaner.RemoveDefaultArgsOf(D, &getInterpreter()->getSema());
    }
    // Don't need to keep track of cleaned up decls from file.
    m_Map.erase(found);
  }

  AutoloadCallback::~AutoloadCallback() {
  }

  void AutoloadCallback::TransactionCommitted(const Transaction &T) {
    if (T.decls_begin() == T.decls_end())
      return;
    if (T.decls_begin()->m_DGR.isNull())
      return;

    // The first decl must be
    //   extern int __Cling_Autoloading_Map;
    bool HaveAutoloadingMapMarker = false;
    for (auto I = T.decls_begin(), E = T.decls_end();
         !HaveAutoloadingMapMarker && I != E; ++I) {
      if (I->m_Call != cling::Transaction::kCCIHandleTopLevelDecl)
        return;
      for (auto&& D: I->m_DGR) {
        if (isa<EmptyDecl>(D))
          continue;
        else if (auto VD = dyn_cast<VarDecl>(D)) {
          HaveAutoloadingMapMarker
            = VD->hasExternalStorage() && VD->getIdentifier()
              && VD->getName().equals("__Cling_Autoloading_Map");
          if (!HaveAutoloadingMapMarker)
            return;
          break;
        } else
          return;
      }
    }

    if (!HaveAutoloadingMapMarker)
      return;

    AutoloadingVisitor defaultArgsStateCollector;
    Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
    for (auto I = T.decls_begin(), E = T.decls_end(); I != E; ++I)
      for (auto&& D: I->m_DGR)
        defaultArgsStateCollector.TrackDefaultArgStateOf(D, m_Map, PP);
  }

} //end namespace cling
<commit_msg>In AutoloadingVisitor, record and use the fact on whether we are traversing an autolading decl or not.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Manasij Mukherjee  <manasij7479@gmail.com>
// author:  Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

#include "clang/Sema/Sema.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h" // for operator new[](unsigned long, ASTCtx..)
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/AutoloadCallback.h"
#include "cling/Interpreter/Transaction.h"
#include "DeclUnloader.h"

namespace {
  static const char annoTag[] = "$clingAutoload$";
  static const size_t lenAnnoTag = sizeof(annoTag) - 1;
}

using namespace clang;

namespace cling {

  void AutoloadCallback::report(clang::SourceLocation l, llvm::StringRef name,
                                llvm::StringRef header) {
    Sema& sema= m_Interpreter->getSema();

    unsigned id
      = sema.getDiagnostics().getCustomDiagID (DiagnosticsEngine::Level::Warning,
                                                 "Note: '%0' can be found in %1");
/*    unsigned idn //TODO: To be enabled after we have a way to get the full path
      = sema.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Level::Note,
                                                "Type : %0 , Full Path: %1")*/;

    if (header.startswith(llvm::StringRef(annoTag, lenAnnoTag)))
      sema.Diags.Report(l, id) << name << header.drop_front(lenAnnoTag);

  }

  bool AutoloadCallback::LookupObject (TagDecl *t) {
    if (m_ShowSuggestions && t->hasAttr<AnnotateAttr>())
      report(t->getLocation(),t->getNameAsString(),t->getAttr<AnnotateAttr>()->getAnnotation());
    return false;
  }

  class AutoloadingVisitor: public RecursiveASTVisitor<AutoloadingVisitor> {
  private:
    ///\brief Flag determining the visitor's actions. If true, register autoload
    /// entries, i.e. remember the connection between filename and the declaration
    /// that needs to be updated on #include of the filename.
    /// If false, react on an #include by adjusting the forward decls, e.g. by
    /// removing the default tremplate arguments (that will now be provided by
    /// the definition read from the include) and by removing enum declarations
    /// that would otherwise be duplicates.
    bool m_IsStoringState;
    bool m_IsAutloadEntry;  // True during the traversal of an explicitly annotated decl.
    AutoloadCallback::FwdDeclsMap* m_Map;
    clang::Preprocessor* m_PP;
    clang::Sema* m_Sema;
    const clang::FileEntry* m_PrevFE;
    std::string m_PrevFileName;
  private:
    bool IsAutoloadEntry(Decl *D) {
       AnnotateAttr* attr = D->getAttr<AnnotateAttr>();
       if (attr && !attr->isInherited()) {
         llvm::StringRef annotation = attr->getAnnotation();
         assert(!annotation.empty() && "Empty annotation!");
         if (annotation.startswith(llvm::StringRef(annoTag, lenAnnoTag))) {
            // autoload annotation.
            return true;
         }
       }
       return false;
    }

    void InsertIntoAutoloadingState (Decl* decl, llvm::StringRef annotation) {

      assert(!annotation.empty() && "Empty annotation!");
      if (!annotation.startswith(llvm::StringRef(annoTag, lenAnnoTag))) {
        // not an autoload annotation.
        return;
      }

      assert(m_PP);

      const FileEntry* FE = 0;
      SourceLocation fileNameLoc;
      bool isAngled = false;
      const DirectoryLookup* FromDir = 0;
      const FileEntry* FromFile = 0;
      const DirectoryLookup* CurDir = 0;
      llvm::StringRef FileName = annotation.drop_front(lenAnnoTag);
      if (FileName.equals(m_PrevFileName))
        FE = m_PrevFE;
      else {
        FE = m_PP->LookupFile(fileNameLoc, FileName, isAngled,
                              FromDir, FromFile, CurDir, /*SearchPath*/0,
                              /*RelativePath*/ 0, /*suggestedModule*/0,
                              /*SkipCache*/ false, /*OpenFile*/ false,
                              /*CacheFail*/ true);
        m_PrevFE = FE;
        m_PrevFileName = FileName;
      }

      if (FE) {
        auto& Vec = (*m_Map)[FE];
        Vec.push_back(decl);
      } else {
        llvm::errs()
          << "Error in cling::AutoloadingVisitor::InsertIntoAutoloadingState:\n"
          "   Missing FileEntry for " << FileName << "\n";
        if (NamedDecl* ND = dyn_cast<NamedDecl>(decl)) {
          llvm::errs() << "   requested to autoload type ";
          ND->getNameForDiagnostic(llvm::errs(),
                                   ND->getASTContext().getPrintingPolicy(),
                                   true /*qualified*/);
          llvm::errs() << "\n";
        }
      }
    }

  public:
    AutoloadingVisitor():
      m_IsStoringState(false), m_IsAutloadEntry(false), m_Map(0), m_PP(0),
      m_Sema(0), m_PrevFE(0)
    {}

    void RemoveDefaultArgsOf(Decl* D, Sema* S) {
      m_Sema = S;

      auto cursor = D->getMostRecentDecl();
      m_IsAutloadEntry = IsAutoloadEntry(cursor);
      TraverseDecl(cursor);
      while (cursor != D && (cursor = cursor->getPreviousDecl())) {
        m_IsAutloadEntry = IsAutoloadEntry(cursor);
        TraverseDecl(cursor);
      }
      m_IsAutloadEntry = false;
    }

    void TrackDefaultArgStateOf(Decl* D, AutoloadCallback::FwdDeclsMap& map,
                                Preprocessor& PP) {
      m_IsStoringState = true;
      m_Map = ↦
      m_PP = &PP;
      TraverseDecl(D);
      m_PP = 0;
      m_Map = 0;
      m_IsStoringState = false;
    }

    bool shouldVisitTemplateInstantiations() { return true; }

    bool VisitDecl(Decl* D) {
      if (!m_IsStoringState)
        return true;

      if (!D->hasAttr<AnnotateAttr>())
        return true;

      if (AnnotateAttr* attr = D->getAttr<AnnotateAttr>())
        if (!attr->isInherited())
           InsertIntoAutoloadingState(D, attr->getAnnotation());

      return true;
    }

    bool VisitCXXRecordDecl(CXXRecordDecl* D) {
      if (!D->hasAttr<AnnotateAttr>())
        return true;

      if (ClassTemplateDecl* TmplD = D->getDescribedClassTemplate())
        return VisitTemplateDecl(TmplD);
      return true;
    }

    bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl* D) {
      if (m_IsStoringState)
        return true;

      if (m_IsAutloadEntry) {
        if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
          D->removeDefaultArgument();
      } else {
        if (D->hasDefaultArgument() && D->defaultArgumentWasInherited())
          D->removeDefaultArgument();
      }
      return true;
    }

    bool VisitTemplateDecl(TemplateDecl* D) {
      if (D->getTemplatedDecl() &&
          !D->getTemplatedDecl()->hasAttr<AnnotateAttr>())
        return true;

      // If we have a definition we might be about to re-#include the
      // same header containing definition that was #included previously,
      // i.e. we might have multiple fwd decls for the same template.
      // DO NOT remove the defaults here; the definition needs to keep it.
      // (ROOT-7037)
      if (ClassTemplateDecl* CTD = dyn_cast<ClassTemplateDecl>(D))
        if (CXXRecordDecl* TemplatedD = CTD->getTemplatedDecl())
          if (TemplatedD->getDefinition())
            return true;

      for(auto P: D->getTemplateParameters()->asArray())
        TraverseDecl(P);
      return true;
    }

    bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl* D) {
      if (m_IsStoringState)
        return true;

      if (m_IsAutloadEntry) {
        if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
          D->removeDefaultArgument();
      } else {
        if (D->hasDefaultArgument() && D->defaultArgumentWasInherited())
          D->removeDefaultArgument();
      }
      return true;
    }

    bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl* D) {
      if (m_IsStoringState)
        return true;

      if (m_IsAutloadEntry) {
        if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
          D->removeDefaultArgument();
      } else {
        if (D->hasDefaultArgument() && D->defaultArgumentWasInherited())
          D->removeDefaultArgument();
      }
      return true;
    }

    bool VisitParmVarDecl(ParmVarDecl* D) {
      if (m_IsStoringState)
        return true;

      if (m_IsAutloadEntry) {
        if (D->hasDefaultArg() && !D->hasInheritedDefaultArg())
          D->setDefaultArg(nullptr);
      } else {
        if (D->hasDefaultArg() && D->hasInheritedDefaultArg())
          D->setDefaultArg(nullptr);
      }
      return true;
    }

    bool VisitEnumDecl(EnumDecl* D) {
      if (m_IsStoringState)
        return true;

      // Now that we will read the full enum, unload the forward decl.
      if (IsAutoloadEntry(D))
        UnloadDecl(m_Sema, D);
      return true;
    }
  };

  void AutoloadCallback::InclusionDirective(clang::SourceLocation HashLoc,
                          const clang::Token &IncludeTok,
                          llvm::StringRef FileName,
                          bool IsAngled,
                          clang::CharSourceRange FilenameRange,
                          const clang::FileEntry *File,
                          llvm::StringRef SearchPath,
                          llvm::StringRef RelativePath,
                          const clang::Module *Imported) {
    // If File is 0 this means that the #included file doesn't exist.
    if (!File)
      return;

    auto found = m_Map.find(File);
    if (found == m_Map.end())
     return; // nothing to do, file not referred in any annotation

    AutoloadingVisitor defaultArgsCleaner;
    for (auto D : found->second) {
      defaultArgsCleaner.RemoveDefaultArgsOf(D, &getInterpreter()->getSema());
    }
    // Don't need to keep track of cleaned up decls from file.
    m_Map.erase(found);
  }

  AutoloadCallback::~AutoloadCallback() {
  }

  void AutoloadCallback::TransactionCommitted(const Transaction &T) {
    if (T.decls_begin() == T.decls_end())
      return;
    if (T.decls_begin()->m_DGR.isNull())
      return;

    // The first decl must be
    //   extern int __Cling_Autoloading_Map;
    bool HaveAutoloadingMapMarker = false;
    for (auto I = T.decls_begin(), E = T.decls_end();
         !HaveAutoloadingMapMarker && I != E; ++I) {
      if (I->m_Call != cling::Transaction::kCCIHandleTopLevelDecl)
        return;
      for (auto&& D: I->m_DGR) {
        if (isa<EmptyDecl>(D))
          continue;
        else if (auto VD = dyn_cast<VarDecl>(D)) {
          HaveAutoloadingMapMarker
            = VD->hasExternalStorage() && VD->getIdentifier()
              && VD->getName().equals("__Cling_Autoloading_Map");
          if (!HaveAutoloadingMapMarker)
            return;
          break;
        } else
          return;
      }
    }

    if (!HaveAutoloadingMapMarker)
      return;

    AutoloadingVisitor defaultArgsStateCollector;
    Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
    for (auto I = T.decls_begin(), E = T.decls_end(); I != E; ++I)
      for (auto&& D: I->m_DGR)
        defaultArgsStateCollector.TrackDefaultArgStateOf(D, m_Map, PP);
  }

} //end namespace cling
<|endoftext|>


<commit_before>#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Path.h"
#include "clang/Sema/Sema.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/AST.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/AutoloadCallback.h"
#include "cling/Interpreter/Transaction.h"

using namespace clang;

namespace cling {

  void AutoloadCallback::report(clang::SourceLocation l,std::string name,std::string header) {
    Sema& sema= m_Interpreter->getSema();

    unsigned id
      = sema.getDiagnostics().getCustomDiagID (DiagnosticsEngine::Level::Warning,
                                                 "Note: '%0' can be found in %1");
/*    unsigned idn //TODO: To be enabled after we have a way to get the full path
      = sema.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Level::Note,
                                                "Type : %0 , Full Path: %1")*/;

    sema.Diags.Report(l, id) << name << header;

  }

  bool AutoloadCallback::LookupObject (TagDecl *t) {
    if (t->hasAttr<AnnotateAttr>())
      report(t->getLocation(),t->getNameAsString(),t->getAttr<AnnotateAttr>()->getAnnotation());
    return false;
  }

  class DefaultArgRemover: public RecursiveASTVisitor<DefaultArgRemover> {
  public:
    void Remove(Decl* D) {
      TraverseDecl(D);
    }

    bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl* D) {
      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl* D) {
      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitParmVarDecl(ParmVarDecl* D) {
      if (D->hasDefaultArg())
        D->setDefaultArg(nullptr);
      return true;
    }
  };

  void AutoloadCallback::InclusionDirective(clang::SourceLocation HashLoc,
                          const clang::Token &IncludeTok,
                          llvm::StringRef FileName,
                          bool IsAngled,
                          clang::CharSourceRange FilenameRange,
                          const clang::FileEntry *File,
                          llvm::StringRef SearchPath,
                          llvm::StringRef RelativePath,
                          const clang::Module *Imported) {
    assert(File && "Must have a valid File");

    auto iterator = m_Map.find(File->getUID());
    if (iterator == m_Map.end())
      return; // nothing to do, file not referred in any annotation
    if(iterator->second.Included)
      return; // nothing to do, file already included once

    iterator->second.Included = true;

    DefaultArgRemover defaultArgsCleaner;

    for(clang::Decl* decl : iterator->second.Decls) {
      decl->dropAttrs();
      defaultArgsCleaner.Remove(decl);
    }

  }

  AutoloadCallback::AutoloadCallback(Interpreter* interp) :
    InterpreterCallbacks(interp,true,false,true), m_Interpreter(interp){

  }

  AutoloadCallback::~AutoloadCallback() {
  }

  void AutoloadCallback::InsertIntoAutoloadingState (clang::Decl* decl,
                                                     std::string annotation) {

    assert(annotation != "" && "Empty annotation!");

    clang::Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
    const FileEntry* FE = 0;
    SourceLocation fileNameLoc;
    bool isAngled = false;
    const DirectoryLookup* LookupFrom = 0;
    const DirectoryLookup* CurDir = 0;

    FE = PP.LookupFile(fileNameLoc, annotation, isAngled, LookupFrom, CurDir,
                       /*SearchPath*/0, /*RelativePath*/ 0,
                       /*suggestedModule*/0, /*SkipCache*/false,
                       /*OpenFile*/ false, /*CacheFail*/ false);

    assert(FE && "Must have a valid FileEntry");

    auto& stateMap = m_Map;
    auto iterator = stateMap.find(FE->getUID());

    if(iterator == stateMap.end())
      stateMap[FE->getUID()] = FileInfo();

    stateMap[FE->getUID()].Decls.push_back(decl);
  }

  void AutoloadCallback::HandleNamespace(NamespaceDecl* NS) {
    std::vector<clang::Decl*> decls;
    for(auto dit = NS->decls_begin(); dit != NS->decls_end(); ++dit)
      decls.push_back(*dit);
    HandleDeclVector(decls);
  }

  void AutoloadCallback::HandleClassTemplate(ClassTemplateDecl* CT) {
    CXXRecordDecl* cxxr = CT->getTemplatedDecl();
    if(cxxr->hasAttr<AnnotateAttr>())
      InsertIntoAutoloadingState(CT,cxxr->getAttr<AnnotateAttr>()->getAnnotation());
  }
  void AutoloadCallback::HandleFunction(FunctionDecl *F) {
    if(F->hasAttr<AnnotateAttr>())
      InsertIntoAutoloadingState(F,F->getAttr<AnnotateAttr>()->getAnnotation());
  }

  void AutoloadCallback::HandleDeclVector(std::vector<clang::Decl*> Decls) {
    for(auto decl : Decls ) {
      if(auto ct = llvm::dyn_cast<ClassTemplateDecl>(decl))
        HandleClassTemplate(ct);
      if(auto ns = llvm::dyn_cast<NamespaceDecl>(decl))
        HandleNamespace(ns);
      if(auto f = llvm::dyn_cast<FunctionDecl>(decl))
        HandleFunction(f);
    }
  }
  void AutoloadCallback::TransactionCommitted(const Transaction &T) {
      for (Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
           I != E; ++I) {
        Transaction::DelayCallInfo DCI = *I;

        if (DCI.m_Call != Transaction::kCCIHandleTopLevelDecl)
          continue;

        std::vector<clang::Decl*> decls;
        for (DeclGroupRef::iterator J = DCI.m_DGR.begin(),
               JE = DCI.m_DGR.end(); J != JE; ++J) {
          decls.push_back(*J);
        }
        HandleDeclVector(decls);
      }
  }

}//end namespace cling
<commit_msg>Indents.<commit_after>#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Path.h"
#include "clang/Sema/Sema.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/AST.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/AutoloadCallback.h"
#include "cling/Interpreter/Transaction.h"

using namespace clang;

namespace cling {

  void AutoloadCallback::report(clang::SourceLocation l,std::string name,std::string header) {
    Sema& sema= m_Interpreter->getSema();

    unsigned id
      = sema.getDiagnostics().getCustomDiagID (DiagnosticsEngine::Level::Warning,
                                                 "Note: '%0' can be found in %1");
/*    unsigned idn //TODO: To be enabled after we have a way to get the full path
      = sema.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Level::Note,
                                                "Type : %0 , Full Path: %1")*/;

    sema.Diags.Report(l, id) << name << header;

  }

  bool AutoloadCallback::LookupObject (TagDecl *t) {
    if (t->hasAttr<AnnotateAttr>())
      report(t->getLocation(),t->getNameAsString(),t->getAttr<AnnotateAttr>()->getAnnotation());
    return false;
  }

  class DefaultArgRemover: public RecursiveASTVisitor<DefaultArgRemover> {
  public:
    void Remove(Decl* D) {
      TraverseDecl(D);
    }

    bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl* D) {
      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl* D) {
      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitParmVarDecl(ParmVarDecl* D) {
      if (D->hasDefaultArg())
        D->setDefaultArg(nullptr);
      return true;
    }
  };

  void AutoloadCallback::InclusionDirective(clang::SourceLocation HashLoc,
                          const clang::Token &IncludeTok,
                          llvm::StringRef FileName,
                          bool IsAngled,
                          clang::CharSourceRange FilenameRange,
                          const clang::FileEntry *File,
                          llvm::StringRef SearchPath,
                          llvm::StringRef RelativePath,
                          const clang::Module *Imported) {
    assert(File && "Must have a valid File");

    auto iterator = m_Map.find(File->getUID());
    if (iterator == m_Map.end())
      return; // nothing to do, file not referred in any annotation
    if(iterator->second.Included)
      return; // nothing to do, file already included once

    iterator->second.Included = true;

    DefaultArgRemover defaultArgsCleaner;

    for(clang::Decl* decl : iterator->second.Decls) {
      decl->dropAttrs();
      defaultArgsCleaner.Remove(decl);
    }

  }

  AutoloadCallback::AutoloadCallback(Interpreter* interp) :
    InterpreterCallbacks(interp,true,false,true), m_Interpreter(interp){

  }

  AutoloadCallback::~AutoloadCallback() {
  }

  void AutoloadCallback::InsertIntoAutoloadingState (clang::Decl* decl,
                                                     std::string annotation) {

    assert(annotation != "" && "Empty annotation!");

    clang::Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
    const FileEntry* FE = 0;
    SourceLocation fileNameLoc;
    bool isAngled = false;
    const DirectoryLookup* LookupFrom = 0;
    const DirectoryLookup* CurDir = 0;

    FE = PP.LookupFile(fileNameLoc, annotation, isAngled, LookupFrom, CurDir,
                       /*SearchPath*/0, /*RelativePath*/ 0,
                       /*suggestedModule*/0, /*SkipCache*/false,
                       /*OpenFile*/ false, /*CacheFail*/ false);

    assert(FE && "Must have a valid FileEntry");

    auto& stateMap = m_Map;
    auto iterator = stateMap.find(FE->getUID());

    if(iterator == stateMap.end())
      stateMap[FE->getUID()] = FileInfo();

    stateMap[FE->getUID()].Decls.push_back(decl);
  }

  void AutoloadCallback::HandleNamespace(NamespaceDecl* NS) {
    std::vector<clang::Decl*> decls;
    for(auto dit = NS->decls_begin(); dit != NS->decls_end(); ++dit)
      decls.push_back(*dit);
    HandleDeclVector(decls);
  }

  void AutoloadCallback::HandleClassTemplate(ClassTemplateDecl* CT) {
    CXXRecordDecl* cxxr = CT->getTemplatedDecl();
    if(cxxr->hasAttr<AnnotateAttr>())
      InsertIntoAutoloadingState(CT,cxxr->getAttr<AnnotateAttr>()->getAnnotation());
  }
  void AutoloadCallback::HandleFunction(FunctionDecl *F) {
    if(F->hasAttr<AnnotateAttr>())
      InsertIntoAutoloadingState(F,F->getAttr<AnnotateAttr>()->getAnnotation());
  }

  void AutoloadCallback::HandleDeclVector(std::vector<clang::Decl*> Decls) {
    for(auto decl : Decls ) {
      if(auto ct = llvm::dyn_cast<ClassTemplateDecl>(decl))
        HandleClassTemplate(ct);
      if(auto ns = llvm::dyn_cast<NamespaceDecl>(decl))
        HandleNamespace(ns);
      if(auto f = llvm::dyn_cast<FunctionDecl>(decl))
        HandleFunction(f);
    }
  }
  void AutoloadCallback::TransactionCommitted(const Transaction &T) {
    for (Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
         I != E; ++I) {
      Transaction::DelayCallInfo DCI = *I;

      if (DCI.m_Call != Transaction::kCCIHandleTopLevelDecl)
        continue;

      std::vector<clang::Decl*> decls;
      for (DeclGroupRef::iterator J = DCI.m_DGR.begin(),
             JE = DCI.m_DGR.end(); J != JE; ++J) {
        decls.push_back(*J);
      }
      HandleDeclVector(decls);
    }
  }

} //end namespace cling
<|endoftext|>


<commit_before>/******************************************************
 * This is the main file for the mips1 ArchC model    *
 * This file is automatically generated by ArchC      *
 * WITHOUT WARRANTY OF ANY KIND, either express       *
 * or implied.                                        *
 * For more information on ArchC, please visit:       *
 * http://www.archc.org                               *
 *                                                    *
 * The ArchC Team                                     *
 * Computer Systems Laboratory (LSC)                  *
 * IC-UNICAMP                                         *
 * http://www.lsc.ic.unicamp.br                       *
 ******************************************************/

// Rodolfo editou aqui
//
const char *project_name="mips1";
const char *project_file="mips1.ac";
const char *archc_version="2.0beta1";
const char *archc_options="-abi -dy ";

#include  <systemc.h>
#include  "mips1.H"
#include  "ac_tlm_mem.h"
#include  "ac_tlm_bus.h"
#include  "ac_tlm_protocol.H"

using user::ac_tlm_mem;
using user::ac_tlm_bus;

static mips1* mips1_proc1;
static mips1* mips1_proc2;
static mips1* mips1_proc3;
static mips1* mips1_proc4;
static mips1* mips1_proc5;
static mips1* mips1_proc6;
static mips1* mips1_proc7;
static mips1* mips1_proc8;

char **
duplicate_argv(int argc, char **argv) {
  char **new_argv = (char **) malloc(argc * sizeof(char *));
  for (int i = 0; i < argc; ++i) {
    new_argv[i] = strdup(argv[i]);
  }

  return new_argv;
}

ac_tlm_rsp wake_up_processor( const ac_tlm_req &request ) {
    ac_tlm_rsp response;

    if (request.addr != 0xFFFFFF) {
      response.status = ERROR;
      return response;
    }

    uint8_t mask = (request.data & 0xFF000000) >> 24;
    if (mask & 1) {// acordar processador 1
      mips1_proc1->ac_wait_sig = 0;
    }

    if (mask & (1 << 1)) {// acordar processador 2
      mips1_proc2->ac_wait_sig = 0;
    }

    if (mask & (1 << 2)) {// acordar processador 3
      mips1_proc3->ac_wait_sig = 0;
    }

    if (mask & (1 << 3)) {// acordar processador 4
      mips1_proc4->ac_wait_sig = 0;
    }

    if (mask & (1 << 4)) {// acordar processador 5
      mips1_proc5->ac_wait_sig = 0;
    }

    if (mask & (1 << 5)) {// acordar processador 6
      mips1_proc6->ac_wait_sig = 0;
    }

    if (mask & (1 << 6)) {// acordar processador 7
      mips1_proc7->ac_wait_sig = 0;
    }

    if (mask & (1 << 7)) {// acordar processador 8
      mips1_proc8->ac_wait_sig = 0;
    }

    response.status = SUCCESS;
    return response;
}

int sc_main(int ac, char *av[])
{

  //!  ISA simulator
  mips1_proc1 = new mips1("mips1");
  mips1_proc2 = new mips1("mips2");
  mips1_proc3 = new mips1("mips3");
  mips1_proc4 = new mips1("mips4");
  mips1_proc5 = new mips1("mips5");
  mips1_proc6 = new mips1("mips6");
  mips1_proc7 = new mips1("mips7");
  mips1_proc8 = new mips1("mips8");

  ac_tlm_mem mem("mem");
  ac_tlm_bus mem_bus("bus", wake_up_processor);

#ifdef AC_DEBUG
  ac_trace("mips1_proc1.trace");
#endif

  mips1_proc1->DM_port(mem_bus.target_export[0]);
  mips1_proc2->DM_port(mem_bus.target_export[1]);
  mips1_proc3->DM_port(mem_bus.target_export[2]);
  mips1_proc4->DM_port(mem_bus.target_export[3]);
  mips1_proc5->DM_port(mem_bus.target_export[4]);
  mips1_proc6->DM_port(mem_bus.target_export[5]);
  mips1_proc7->DM_port(mem_bus.target_export[6]);
  mips1_proc8->DM_port(mem_bus.target_export[7]);

  mem_bus.DM_port(mem.target_export);

  mips1_proc1->init(ac, duplicate_argv(ac, av));
  mips1_proc2->init(ac, duplicate_argv(ac, av));
  mips1_proc3->init(ac, duplicate_argv(ac, av));
  mips1_proc4->init(ac, duplicate_argv(ac, av));
  mips1_proc5->init(ac, duplicate_argv(ac, av));
  mips1_proc6->init(ac, duplicate_argv(ac, av));
  mips1_proc7->init(ac, duplicate_argv(ac, av));
  mips1_proc8->init(ac, duplicate_argv(ac, av));
  cerr << endl;

  mips1_proc1->ac_wait_sig = 0; // bootstrap processor
  mips1_proc2->ac_wait_sig = 1;
  mips1_proc3->ac_wait_sig = 1;
  mips1_proc4->ac_wait_sig = 1;
  mips1_proc5->ac_wait_sig = 1;
  mips1_proc6->ac_wait_sig = 1;
  mips1_proc7->ac_wait_sig = 1;
  mips1_proc8->ac_wait_sig = 1;
  sc_start();

  mips1_proc1->PrintStat();
  cerr << endl;

#ifdef AC_STATS
  mips1_proc1->ac_sim_stats.time = sc_simulation_time();
  mips1_proc1->ac_sim_stats.print();
#endif

#ifdef AC_DEBUG
  ac_close_trace();
#endif

  return mips1_proc1->ac_exit_status;
}
<commit_msg>Delete processors.<commit_after>/******************************************************
 * This is the main file for the mips1 ArchC model    *
 * This file is automatically generated by ArchC      *
 * WITHOUT WARRANTY OF ANY KIND, either express       *
 * or implied.                                        *
 * For more information on ArchC, please visit:       *
 * http://www.archc.org                               *
 *                                                    *
 * The ArchC Team                                     *
 * Computer Systems Laboratory (LSC)                  *
 * IC-UNICAMP                                         *
 * http://www.lsc.ic.unicamp.br                       *
 ******************************************************/

// Rodolfo editou aqui
//
const char *project_name="mips1";
const char *project_file="mips1.ac";
const char *archc_version="2.0beta1";
const char *archc_options="-abi -dy ";

#include  <systemc.h>
#include  "mips1.H"
#include  "ac_tlm_mem.h"
#include  "ac_tlm_bus.h"
#include  "ac_tlm_protocol.H"

using user::ac_tlm_mem;
using user::ac_tlm_bus;

static mips1* mips1_proc1;
static mips1* mips1_proc2;
static mips1* mips1_proc3;
static mips1* mips1_proc4;
static mips1* mips1_proc5;
static mips1* mips1_proc6;
static mips1* mips1_proc7;
static mips1* mips1_proc8;

char **
duplicate_argv(int argc, char **argv) {
  char **new_argv = (char **) malloc(argc * sizeof(char *));
  for (int i = 0; i < argc; ++i) {
    new_argv[i] = strdup(argv[i]);
  }

  return new_argv;
}

ac_tlm_rsp wake_up_processor( const ac_tlm_req &request ) {
    ac_tlm_rsp response;

    if (request.addr != 0xFFFFFF) {
      response.status = ERROR;
      return response;
    }

    uint8_t mask = (request.data & 0xFF000000) >> 24;
    if (mask & 1) {// acordar processador 1
      mips1_proc1->ac_wait_sig = 0;
    }

    if (mask & (1 << 1)) {// acordar processador 2
      mips1_proc2->ac_wait_sig = 0;
    }

    if (mask & (1 << 2)) {// acordar processador 3
      mips1_proc3->ac_wait_sig = 0;
    }

    if (mask & (1 << 3)) {// acordar processador 4
      mips1_proc4->ac_wait_sig = 0;
    }

    if (mask & (1 << 4)) {// acordar processador 5
      mips1_proc5->ac_wait_sig = 0;
    }

    if (mask & (1 << 5)) {// acordar processador 6
      mips1_proc6->ac_wait_sig = 0;
    }

    if (mask & (1 << 6)) {// acordar processador 7
      mips1_proc7->ac_wait_sig = 0;
    }

    if (mask & (1 << 7)) {// acordar processador 8
      mips1_proc8->ac_wait_sig = 0;
    }

    response.status = SUCCESS;
    return response;
}

int sc_main(int ac, char *av[])
{

  //!  ISA simulator
  mips1_proc1 = new mips1("mips1");
  mips1_proc2 = new mips1("mips2");
  mips1_proc3 = new mips1("mips3");
  mips1_proc4 = new mips1("mips4");
  mips1_proc5 = new mips1("mips5");
  mips1_proc6 = new mips1("mips6");
  mips1_proc7 = new mips1("mips7");
  mips1_proc8 = new mips1("mips8");

  ac_tlm_mem mem("mem");
  ac_tlm_bus mem_bus("bus", wake_up_processor);

#ifdef AC_DEBUG
  ac_trace("mips1_proc1.trace");
#endif

  mips1_proc1->DM_port(mem_bus.target_export[0]);
  mips1_proc2->DM_port(mem_bus.target_export[1]);
  mips1_proc3->DM_port(mem_bus.target_export[2]);
  mips1_proc4->DM_port(mem_bus.target_export[3]);
  mips1_proc5->DM_port(mem_bus.target_export[4]);
  mips1_proc6->DM_port(mem_bus.target_export[5]);
  mips1_proc7->DM_port(mem_bus.target_export[6]);
  mips1_proc8->DM_port(mem_bus.target_export[7]);

  mem_bus.DM_port(mem.target_export);

  mips1_proc1->init(ac, duplicate_argv(ac, av));
  mips1_proc2->init(ac, duplicate_argv(ac, av));
  mips1_proc3->init(ac, duplicate_argv(ac, av));
  mips1_proc4->init(ac, duplicate_argv(ac, av));
  mips1_proc5->init(ac, duplicate_argv(ac, av));
  mips1_proc6->init(ac, duplicate_argv(ac, av));
  mips1_proc7->init(ac, duplicate_argv(ac, av));
  mips1_proc8->init(ac, duplicate_argv(ac, av));
  cerr << endl;

  mips1_proc1->ac_wait_sig = 0; // bootstrap processor
  mips1_proc2->ac_wait_sig = 1;
  mips1_proc3->ac_wait_sig = 1;
  mips1_proc4->ac_wait_sig = 1;
  mips1_proc5->ac_wait_sig = 1;
  mips1_proc6->ac_wait_sig = 1;
  mips1_proc7->ac_wait_sig = 1;
  mips1_proc8->ac_wait_sig = 1;
  sc_start();

  mips1_proc1->PrintStat();
  cerr << endl;

#ifdef AC_STATS
  mips1_proc1->ac_sim_stats.time = sc_simulation_time();
  mips1_proc1->ac_sim_stats.print();
#endif

#ifdef AC_DEBUG
  ac_close_trace();
#endif

  int retval = mips1_proc1->ac_exit_status;
  delete mips1_proc1;
  delete mips1_proc2;
  delete mips1_proc3;
  delete mips1_proc4;
  delete mips1_proc5;
  delete mips1_proc6;
  delete mips1_proc7;
  delete mips1_proc8;

  return retval;
}
<|endoftext|>


<commit_before><commit_msg>changed SIZE_INSTR to reflect change to instructions.txt<commit_after><|endoftext|>


<commit_before>//===- PerformanceInliner.cpp - Basic cost based inlining for performance -===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-inliner"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/CallGraph.h"
#include "swift/SIL/SILModule.h"
#include "swift/SILPasses/Passes.h"
#include "swift/SILPasses/Utils/SILInliner.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/ADT/MapVector.h"
using namespace swift;

static llvm::cl::opt<unsigned>
InlineCostThreshold("sil-inline-threshold", llvm::cl::Hidden,
                    llvm::cl::init(50));

STATISTIC(NumFunctionsInlined, "Number of functions inlined");

//===----------------------------------------------------------------------===//
//                            Call Graph Creation
//===----------------------------------------------------------------------===//

/// \brief Returns a SILFunction if this ApplyInst calls a recognizable function
/// that is legal to inline.
static SILFunction *getInlinableFunction(ApplyInst *AI) {
  // Avoid substituion lists, we don't support them.
  if (AI->hasSubstitutions())
    return nullptr;

  auto *FRI = dyn_cast<FunctionRefInst>(AI->getCallee().getDef());
  if (!FRI)
    return nullptr;

  SILFunction *F = FRI->getReferencedFunction();

  if (F->empty() || F->isExternalDeclaration()) {
    DEBUG(llvm::dbgs() << "  Can't inline " << F->getName() << ".\n");
    return nullptr;
  }

  DEBUG(llvm::dbgs() << "  Can inline " << F->getName() << ".\n");
  return F;
}

/// Return the bottom up call-graph order for module M. Notice that we don't
/// include functions that don't participate in any call (caller or callee).
static void TopDownCallGraphOrder(SILModule *M,
                                  std::vector<SILFunction *> &order) {
  CallGraphSorter<SILFunction *> sorter;

  // Construct the call graph, mapping callee to callers to that the resulting
  // topological ordering has callees before callers.
  //
  // *NOTE* From the typical callgraph perspective, we are inserting edges in
  // reverse.
  for (auto &Caller : *M)
    for (auto &BB : Caller)
      for (auto &I : BB)
        if (FunctionRefInst *FRI = dyn_cast<FunctionRefInst>(&I)) {
          SILFunction *Callee = FRI->getReferencedFunction();
          sorter.addEdge(Callee, &Caller);
        }

  // Perform the topological sorting.
  sorter.sort(order);
}

//===----------------------------------------------------------------------===//
//                                 Cost Model
//===----------------------------------------------------------------------===//

/// For now just assume that every SIL instruction is one to one with an LLVM
/// instruction. This is of course very much so not true.
///
/// TODO: Fill this out.
static unsigned instructionInlineCost(SILInstruction &I) {
  switch (I.getKind()) {
    case ValueKind::FunctionRefInst:
    case ValueKind::BuiltinFunctionRefInst:
    case ValueKind::GlobalAddrInst:
    case ValueKind::SILGlobalAddrInst:
    case ValueKind::IntegerLiteralInst:
    case ValueKind::FloatLiteralInst:
    case ValueKind::DebugValueInst:
    case ValueKind::DebugValueAddrInst:
      return 0;
    case ValueKind::TupleElementAddrInst:
    case ValueKind::StructElementAddrInst: {
      // A gep whose operand is a gep with no other users will get folded by
      // LLVM into one gep implying the second should be free.
      SILValue Op = I.getOperand(0);
      if ((Op->getKind() == ValueKind::TupleElementAddrInst ||
           Op->getKind() == ValueKind::StructElementAddrInst) &&
          Op->hasOneUse())
        return 0;
    }
    default:
      return 1;
  }
}

/// \brief Returns the inlining cost of the function.
static unsigned getFunctionCost(SILFunction *F) {
  DEBUG(llvm::dbgs() << "  Calculating cost for " << F->getName() << ".\n");

  if (F->isTransparent() == IsTransparent_t::IsTransparent)
    return 0;

  unsigned Cost = 0;
  for (auto &BB : *F) {
    for (auto &I : BB) {
      Cost += instructionInlineCost(I);

      // If i is greater than the InlineCostThreshold, we already know we are
      // not going to inline this given function, so there is no point in
      // continuing to visit instructions.
      if (Cost > InlineCostThreshold) {
        DEBUG(llvm::dbgs() << "  Cost too high.\n");
        return Cost;
      }
    }
  }

  DEBUG(llvm::dbgs() << "  Found cost: " << Cost << "\n");
  return Cost;
}

//===----------------------------------------------------------------------===//
//                                  Inliner
//===----------------------------------------------------------------------===//

/// Attempt to inline all calls smaller than our threshold into F until.
static void inlineCallsIntoFunction(SILFunction *Caller) {
  SILInliner Inliner(*Caller, SILInliner::InlineKind::PerformanceInline);

  DEBUG(llvm::dbgs() << "Visiting Function: " << Caller->getName() << "\n");

  llvm::SmallVector<ApplyInst*, 8> CallSites;

  // Collect all of the ApplyInsts in this function. We will be changing the
  // control flow and collecting the AIs simplifies the scan.
  for (auto &BB : *Caller) {
    auto I = BB.begin(), E = BB.end();
    while (I != E) {
      // Check if this is a call site.
      ApplyInst *AI = dyn_cast<ApplyInst>(I++);
      if (AI)
        CallSites.push_back(AI);
    }
  }

  for (auto AI : CallSites) {
    DEBUG(llvm::dbgs() << "  Found call site:" <<  *AI);

    // Get the callee.
    SILFunction *Callee = getInlinableFunction(AI);
    if (!Callee)
      continue;

    DEBUG(llvm::dbgs() << "  Found callee:" <<  Callee->getName() << ".\n");

    // Prevent circular inlining.
    if (Callee == Caller) {
      DEBUG(llvm::dbgs() << "  Skipping recursive calls.\n");
      continue;
    }

    // Calculate the inlining cost of the callee.
    unsigned CalleeCost = getFunctionCost(Callee);

    if (CalleeCost > InlineCostThreshold) {
      DEBUG(llvm::dbgs() << "  Function too big to inline. Skipping.\n");
      continue;
    }

    // Add the arguments from AI into a SILValue list.
    SmallVector<SILValue, 8> Args;
    for (const auto &Arg : AI->getArguments())
    Args.push_back(Arg);

    // Ok, we are within budget. Attempt to inline.
    DEBUG(llvm::dbgs() << "  Inlining " << Callee->getName() << " Into " <<
          Caller->getName() << "\n");

    // We already moved the iterator to the next instruction because the AI
    // will be erased by the inliner. Notice that we will skip all of the
    // newly inlined ApplyInsts. That's okay because we will visit them in
    // our next invocation of the inliner.
    Inliner.inlineFunction(AI, Callee, ArrayRef<Substitution>(), Args);
    NumFunctionsInlined++;
  }
}

//===----------------------------------------------------------------------===//
//                              Top Level Driver
//===----------------------------------------------------------------------===//

void swift::performSILPerformanceInlining(SILModule *M) {
  DEBUG(llvm::dbgs() << "*** SIL Performance Inlining ***\n\n");

  if (InlineCostThreshold == 0) {
    DEBUG(llvm::dbgs() << "*** The SIL performance Inliner is disabled ***\n");
    return;
  }

  // Collect a call-graph bottom-up list of functions.
  std::vector<SILFunction *> Worklist;
  TopDownCallGraphOrder(M, Worklist);

  // For each function in the worklist, attempt to inline its list of apply
  // inst.
  while (Worklist.size()) {
    SILFunction *F = Worklist.back();
    Worklist.pop_back();

    // Do not inline into transparent functions. This is exposing a diagnostics
    // bug. We will still inline after we perform mandatory inlining of the
    // transparent function.
    inlineCallsIntoFunction(F);
  }
}
<commit_msg>PerformanceInliner: struct, tuple, struct_extract, and tuple_extract are free.<commit_after>//===- PerformanceInliner.cpp - Basic cost based inlining for performance -===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-inliner"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/CallGraph.h"
#include "swift/SIL/SILModule.h"
#include "swift/SILPasses/Passes.h"
#include "swift/SILPasses/Utils/SILInliner.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/ADT/MapVector.h"
using namespace swift;

static llvm::cl::opt<unsigned>
InlineCostThreshold("sil-inline-threshold", llvm::cl::Hidden,
                    llvm::cl::init(50));

STATISTIC(NumFunctionsInlined, "Number of functions inlined");

//===----------------------------------------------------------------------===//
//                            Call Graph Creation
//===----------------------------------------------------------------------===//

/// \brief Returns a SILFunction if this ApplyInst calls a recognizable function
/// that is legal to inline.
static SILFunction *getInlinableFunction(ApplyInst *AI) {
  // Avoid substituion lists, we don't support them.
  if (AI->hasSubstitutions())
    return nullptr;

  auto *FRI = dyn_cast<FunctionRefInst>(AI->getCallee().getDef());
  if (!FRI)
    return nullptr;

  SILFunction *F = FRI->getReferencedFunction();

  if (F->empty() || F->isExternalDeclaration()) {
    DEBUG(llvm::dbgs() << "  Can't inline " << F->getName() << ".\n");
    return nullptr;
  }

  DEBUG(llvm::dbgs() << "  Can inline " << F->getName() << ".\n");
  return F;
}

/// Return the bottom up call-graph order for module M. Notice that we don't
/// include functions that don't participate in any call (caller or callee).
static void TopDownCallGraphOrder(SILModule *M,
                                  std::vector<SILFunction *> &order) {
  CallGraphSorter<SILFunction *> sorter;

  // Construct the call graph, mapping callee to callers to that the resulting
  // topological ordering has callees before callers.
  //
  // *NOTE* From the typical callgraph perspective, we are inserting edges in
  // reverse.
  for (auto &Caller : *M)
    for (auto &BB : Caller)
      for (auto &I : BB)
        if (FunctionRefInst *FRI = dyn_cast<FunctionRefInst>(&I)) {
          SILFunction *Callee = FRI->getReferencedFunction();
          sorter.addEdge(Callee, &Caller);
        }

  // Perform the topological sorting.
  sorter.sort(order);
}

//===----------------------------------------------------------------------===//
//                                 Cost Model
//===----------------------------------------------------------------------===//

/// For now just assume that every SIL instruction is one to one with an LLVM
/// instruction. This is of course very much so not true.
///
/// TODO: Fill this out.
static unsigned instructionInlineCost(SILInstruction &I) {
  switch (I.getKind()) {
    case ValueKind::FunctionRefInst:
    case ValueKind::BuiltinFunctionRefInst:
    case ValueKind::GlobalAddrInst:
    case ValueKind::SILGlobalAddrInst:
    case ValueKind::IntegerLiteralInst:
    case ValueKind::FloatLiteralInst:
    case ValueKind::DebugValueInst:
    case ValueKind::DebugValueAddrInst:
      return 0;
    case ValueKind::TupleElementAddrInst:
    case ValueKind::StructElementAddrInst: {
      // A gep whose operand is a gep with no other users will get folded by
      // LLVM into one gep implying the second should be free.
      SILValue Op = I.getOperand(0);
      if ((Op->getKind() == ValueKind::TupleElementAddrInst ||
           Op->getKind() == ValueKind::StructElementAddrInst) &&
          Op->hasOneUse())
        return 0;
    }
    // Aggregates are exploded at the IR level; these are effectively no-ops.
    case ValueKind::TupleInst:
    case ValueKind::StructInst:
    case ValueKind::StructExtractInst:
    case ValueKind::TupleExtractInst:
      return 0;

    default:
      return 1;
  }
}

/// \brief Returns the inlining cost of the function.
static unsigned getFunctionCost(SILFunction *F) {
  DEBUG(llvm::dbgs() << "  Calculating cost for " << F->getName() << ".\n");

  if (F->isTransparent() == IsTransparent_t::IsTransparent)
    return 0;

  unsigned Cost = 0;
  for (auto &BB : *F) {
    for (auto &I : BB) {
      Cost += instructionInlineCost(I);

      // If i is greater than the InlineCostThreshold, we already know we are
      // not going to inline this given function, so there is no point in
      // continuing to visit instructions.
      if (Cost > InlineCostThreshold) {
        DEBUG(llvm::dbgs() << "  Cost too high: " << Cost
                           << ". Threshold is " << InlineCostThreshold << ".\n");
        return Cost;
      }
    }
  }

  DEBUG(llvm::dbgs() << "  Found cost: " << Cost << "\n");
  return Cost;
}

//===----------------------------------------------------------------------===//
//                                  Inliner
//===----------------------------------------------------------------------===//

/// Attempt to inline all calls smaller than our threshold into F until.
static void inlineCallsIntoFunction(SILFunction *Caller) {
  SILInliner Inliner(*Caller, SILInliner::InlineKind::PerformanceInline);

  DEBUG(llvm::dbgs() << "Visiting Function: " << Caller->getName() << "\n");

  llvm::SmallVector<ApplyInst*, 8> CallSites;

  // Collect all of the ApplyInsts in this function. We will be changing the
  // control flow and collecting the AIs simplifies the scan.
  for (auto &BB : *Caller) {
    auto I = BB.begin(), E = BB.end();
    while (I != E) {
      // Check if this is a call site.
      ApplyInst *AI = dyn_cast<ApplyInst>(I++);
      if (AI)
        CallSites.push_back(AI);
    }
  }

  for (auto AI : CallSites) {
    DEBUG(llvm::dbgs() << "  Found call site:" <<  *AI);

    // Get the callee.
    SILFunction *Callee = getInlinableFunction(AI);
    if (!Callee)
      continue;

    DEBUG(llvm::dbgs() << "  Found callee:" <<  Callee->getName() << ".\n");

    // Prevent circular inlining.
    if (Callee == Caller) {
      DEBUG(llvm::dbgs() << "  Skipping recursive calls.\n");
      continue;
    }

    // Calculate the inlining cost of the callee.
    unsigned CalleeCost = getFunctionCost(Callee);

    if (CalleeCost > InlineCostThreshold) {
      DEBUG(llvm::dbgs() << "  Function too big to inline. Skipping.\n");
      continue;
    }

    // Add the arguments from AI into a SILValue list.
    SmallVector<SILValue, 8> Args;
    for (const auto &Arg : AI->getArguments())
    Args.push_back(Arg);

    // Ok, we are within budget. Attempt to inline.
    DEBUG(llvm::dbgs() << "  Inlining " << Callee->getName() << " Into " <<
          Caller->getName() << "\n");

    // We already moved the iterator to the next instruction because the AI
    // will be erased by the inliner. Notice that we will skip all of the
    // newly inlined ApplyInsts. That's okay because we will visit them in
    // our next invocation of the inliner.
    Inliner.inlineFunction(AI, Callee, ArrayRef<Substitution>(), Args);
    NumFunctionsInlined++;
  }
}

//===----------------------------------------------------------------------===//
//                              Top Level Driver
//===----------------------------------------------------------------------===//

void swift::performSILPerformanceInlining(SILModule *M) {
  DEBUG(llvm::dbgs() << "*** SIL Performance Inlining ***\n\n");

  if (InlineCostThreshold == 0) {
    DEBUG(llvm::dbgs() << "*** The SIL performance Inliner is disabled ***\n");
    return;
  }

  // Collect a call-graph bottom-up list of functions.
  std::vector<SILFunction *> Worklist;
  TopDownCallGraphOrder(M, Worklist);

  // For each function in the worklist, attempt to inline its list of apply
  // inst.
  while (Worklist.size()) {
    SILFunction *F = Worklist.back();
    Worklist.pop_back();

    // Do not inline into transparent functions. This is exposing a diagnostics
    // bug. We will still inline after we perform mandatory inlining of the
    // transparent function.
    inlineCallsIntoFunction(F);
  }
}
<|endoftext|>