<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/print_web_view_helper.h"

#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/gfx/size.h"
#include "chrome/common/render_messages.h"
#include "chrome/renderer/render_view.h"
#include "grit/generated_resources.h"
#include "printing/units.h"
#include "webkit/api/public/WebScreenInfo.h"
#include "webkit/api/public/WebSize.h"
#include "webkit/glue/webframe.h"
#include "webkit/glue/weburlrequest.h"

#if defined(OS_WIN)
#include "chrome/common/gfx/emf.h"
#include "skia/ext/vector_canvas.h"
#endif

namespace {

// Class that calls the Begin and End print functions on the frame and changes
// the size of the view temporarily to support full page printing..
// Do not serve any events in the time between construction and destruction of
// this class because it will cause flicker.
class PrepareFrameAndViewForPrint {
 public:
  PrepareFrameAndViewForPrint(const ViewMsg_Print_Params& print_params,
                              WebFrame* frame,
                              WebView* web_view)
      : frame_(frame),
        web_view_(web_view),
        expected_pages_count_(0) {
    print_canvas_size_.set_width(
        printing::ConvertUnit(print_params.printable_size.width(),
        static_cast<int>(print_params.dpi),
        print_params.desired_dpi));
    print_canvas_size_.set_height(
        printing::ConvertUnit(print_params.printable_size.height(),
        static_cast<int>(print_params.dpi),
        print_params.desired_dpi));

    // Layout page according to printer page size. Since WebKit shrinks the
    // size of the page automatically (from 125% to 200%) we trick it to
    // think the page is 125% larger so the size of the page is correct for
    // minimum (default) scaling.
    // This is important for sites that try to fill the page.
    gfx::Size print_layout_size(print_canvas_size_);
    print_layout_size.set_height(static_cast<int>(
        static_cast<double>(print_layout_size.height()) * 1.25));

    prev_view_size_ = web_view->GetSize();

    web_view->Resize(print_layout_size);

    frame->BeginPrint(print_canvas_size_, &expected_pages_count_);
  }

  int GetExpectedPageCount() const {
    return expected_pages_count_;
  }

  gfx::Size GetPrintCanvasSize() const {
    return print_canvas_size_;
  }

  ~PrepareFrameAndViewForPrint() {
    frame_->EndPrint();
    web_view_->Resize(prev_view_size_);
  }

 private:
  WebFrame* frame_;
  WebView* web_view_;
  gfx::Size print_canvas_size_;
  gfx::Size prev_view_size_;
  int expected_pages_count_;

  DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint);
};

}  // namespace

void PrintWebViewHelper::SyncPrint(WebFrame* frame) {
#if defined(OS_WIN)
  // Retrieve the default print settings to calculate the expected number of
  // pages.
  ViewMsg_Print_Params default_settings;

  IPC::SyncMessage* msg =
      new ViewHostMsg_GetDefaultPrintSettings(routing_id(), &default_settings);
  if (Send(msg)) {
    msg = NULL;
    // Check if the printer returned any settings, if the settings is empty, we
    // can safely assume there are no printer drivers configured. So we safely
    // terminate.
    if (default_settings.IsEmpty()) {
      RunJavaScriptAlert(frame,
          l10n_util::GetString(IDS_DEFAULT_PRINTER_NOT_FOUND_WARNING_TITLE));
      return;
    }

    // Continue only if the settings are valid.
    if (default_settings.dpi && default_settings.document_cookie) {
      int expected_pages_count = 0;

      // Prepare once to calculate the estimated page count.  This must be in
      // a scope for itself (see comments on PrepareFrameAndViewForPrint).
      {
        PrepareFrameAndViewForPrint prep_frame_view(default_settings,
                                                    frame,
                                                    frame->GetView());
        expected_pages_count = prep_frame_view.GetExpectedPageCount();
        DCHECK(expected_pages_count);
      }

      // Ask the browser to show UI to retrieve the final print settings.
      ViewMsg_PrintPages_Params print_settings;
      // host_window_ may be NULL at this point if the current window is a popup
      // and the print() command has been issued from the parent. The receiver
      // of this message has to deal with this.
      msg = new ViewHostMsg_ScriptedPrint(routing_id(),
                                          render_view_->host_window(),
                                          default_settings.document_cookie,
                                          expected_pages_count,
                                          true,  // has_selection
                                          &print_settings);
      if (Send(msg)) {
        msg = NULL;

        // Printing selection only not supported yet.
        if (print_settings.params.selection_only) {
          NOTIMPLEMENTED();
        }

        // If the settings are invalid, early quit.
        if (print_settings.params.dpi &&
            print_settings.params.document_cookie) {
          // Render the printed pages. It will implicitly revert the document to
          // display CSS media type.
          PrintPages(print_settings, frame);
          // All went well.
          return;
        } else {
          // The user cancelled.
        }
      } else {
        // Send() failed.
        NOTREACHED();
      }
    } else {
      // The user cancelled.
    }
  } else {
    // Send() failed.
    NOTREACHED();
  }
  // TODO(maruel):  bug 1123882 Alert the user that printing failed.
#else  // defined(OS_WIN)
  // TODO(port): print not implemented
  NOTIMPLEMENTED();
#endif
}

void PrintWebViewHelper::PrintPages(const ViewMsg_PrintPages_Params& params,
                                 WebFrame* frame) {
  PrepareFrameAndViewForPrint prep_frame_view(params.params,
                                              frame,
                                              frame->GetView());
  int page_count = prep_frame_view.GetExpectedPageCount();

  Send(new ViewHostMsg_DidGetPrintedPagesCount(routing_id(),
                                               params.params.document_cookie,
                                               page_count));
  if (page_count) {
    ViewMsg_PrintPage_Params page_params;
    page_params.params = params.params;
    if (params.pages.empty()) {
      for (int i = 0; i < page_count; ++i) {
        page_params.page_number = i;
        PrintPage(page_params, prep_frame_view.GetPrintCanvasSize(), frame);
      }
    } else {
      for (size_t i = 0; i < params.pages.size(); ++i) {
        page_params.page_number = params.pages[i];
        PrintPage(page_params, prep_frame_view.GetPrintCanvasSize(), frame);
      }
    }
  }
}

void PrintWebViewHelper::PrintPage(const ViewMsg_PrintPage_Params& params,
                                const gfx::Size& canvas_size,
                                WebFrame* frame) {
#if defined(OS_WIN)
  // Generate a memory-based EMF file. The EMF will use the current screen's
  // DPI.
  gfx::Emf emf;

  emf.CreateDc(NULL, NULL);
  HDC hdc = emf.hdc();
  DCHECK(hdc);
  skia::PlatformDeviceWin::InitializeDC(hdc);
  // Since WebKit extends the page width depending on the magical shrink
  // factor we make sure the canvas covers the worst case scenario
  // (x2.0 currently).  PrintContext will then set the correct clipping region.
  int size_x = static_cast<int>(canvas_size.width() * params.params.max_shrink);
  int size_y = static_cast<int>(canvas_size.height() *
      params.params.max_shrink);
  // Calculate the dpi adjustment.
  float shrink = static_cast<float>(canvas_size.width()) /
      params.params.printable_size.width();
#if 0
  // TODO(maruel): This code is kept for testing until the 100% GDI drawing
  // code is stable. maruels use this code's output as a reference when the
  // GDI drawing code fails.

  // Mix of Skia and GDI based.
  skia::PlatformCanvasWin canvas(size_x, size_y, true);
  canvas.drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
  float webkit_shrink = frame->PrintPage(params.page_number, &canvas);
  if (shrink <= 0) {
    NOTREACHED() << "Printing page " << params.page_number << " failed.";
  } else {
    // Update the dpi adjustment with the "page shrink" calculated in webkit.
    shrink /= webkit_shrink;
  }

  // Create a BMP v4 header that we can serialize.
  BITMAPV4HEADER bitmap_header;
  gfx::CreateBitmapV4Header(size_x, size_y, &bitmap_header);
  const SkBitmap& src_bmp = canvas.getDevice()->accessBitmap(true);
  SkAutoLockPixels src_lock(src_bmp);
  int retval = StretchDIBits(hdc,
                             0,
                             0,
                             size_x, size_y,
                             0, 0,
                             size_x, size_y,
                             src_bmp.getPixels(),
                             reinterpret_cast<BITMAPINFO*>(&bitmap_header),
                             DIB_RGB_COLORS,
                             SRCCOPY);
  DCHECK(retval != GDI_ERROR);
#else
  // 100% GDI based.
  skia::VectorCanvas canvas(hdc, size_x, size_y);
  float webkit_shrink = frame->PrintPage(params.page_number, &canvas);
  if (shrink <= 0) {
    NOTREACHED() << "Printing page " << params.page_number << " failed.";
  } else {
    // Update the dpi adjustment with the "page shrink" calculated in webkit.
    shrink /= webkit_shrink;
  }
#endif

  // Done printing. Close the device context to retrieve the compiled EMF.
  if (!emf.CloseDc()) {
    NOTREACHED() << "EMF failed";
  }

  // Get the size of the compiled EMF.
  unsigned buf_size = emf.GetDataSize();
  DCHECK_GT(buf_size, 128u);
  ViewHostMsg_DidPrintPage_Params page_params;
  page_params.data_size = 0;
  page_params.emf_data_handle = NULL;
  page_params.page_number = params.page_number;
  page_params.document_cookie = params.params.document_cookie;
  page_params.actual_shrink = shrink;
  base::SharedMemory shared_buf;

  // http://msdn2.microsoft.com/en-us/library/ms535522.aspx
  // Windows 2000/XP: When a page in a spooled file exceeds approximately 350
  // MB, it can fail to print and not send an error message.
  if (buf_size < 350*1024*1024) {
    // Allocate a shared memory buffer to hold the generated EMF data.
    if (shared_buf.Create(L"", false, false, buf_size) &&
        shared_buf.Map(buf_size)) {
      // Copy the bits into shared memory.
      if (emf.GetData(shared_buf.memory(), buf_size)) {
        page_params.emf_data_handle = shared_buf.handle();
        page_params.data_size = buf_size;
      } else {
        NOTREACHED() << "GetData() failed";
      }
      shared_buf.Unmap();
    } else {
      NOTREACHED() << "Buffer allocation failed";
    }
  } else {
    NOTREACHED() << "Buffer too large: " << buf_size;
  }
  emf.CloseEmf();
  if (Send(new ViewHostMsg_DuplicateSection(routing_id(),
                                            page_params.emf_data_handle,
                                            &page_params.emf_data_handle))) {
    Send(new ViewHostMsg_DidPrintPage(routing_id(), page_params));
  }
#else  // defined(OS_WIN)
  // TODO(port) implement printing
  NOTIMPLEMENTED();
#endif
}

bool PrintWebViewHelper::Send(IPC::Message* msg) {
  return render_view_->Send(msg);
}

int32 PrintWebViewHelper::routing_id() {
  return render_view_->routing_id();
}

void PrintWebViewHelper::GetWindowRect(WebWidget* webwidget,
                                    WebKit::WebRect* rect) {
  NOTREACHED();
}

void PrintWebViewHelper::DidStopLoading(WebView* webview) {
  NOTREACHED();
}

WebKit::WebScreenInfo PrintWebViewHelper::GetScreenInfo(WebWidget* webwidget) {
  WebKit::WebScreenInfo info;
  NOTREACHED();
  return info;
}

gfx::NativeViewId PrintWebViewHelper::GetContainingView(WebWidget* webwidget) {
  NOTREACHED();
  return gfx::NativeViewId();
}

bool PrintWebViewHelper::IsHidden(WebWidget* webwidget) {
  NOTREACHED();
  return true;
}
<commit_msg>Revert has_selection flag back to false (was testing that this worked and forgot to change back in my last changelist).<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/print_web_view_helper.h"

#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/gfx/size.h"
#include "chrome/common/render_messages.h"
#include "chrome/renderer/render_view.h"
#include "grit/generated_resources.h"
#include "printing/units.h"
#include "webkit/api/public/WebScreenInfo.h"
#include "webkit/api/public/WebSize.h"
#include "webkit/glue/webframe.h"
#include "webkit/glue/weburlrequest.h"

#if defined(OS_WIN)
#include "chrome/common/gfx/emf.h"
#include "skia/ext/vector_canvas.h"
#endif

namespace {

// Class that calls the Begin and End print functions on the frame and changes
// the size of the view temporarily to support full page printing..
// Do not serve any events in the time between construction and destruction of
// this class because it will cause flicker.
class PrepareFrameAndViewForPrint {
 public:
  PrepareFrameAndViewForPrint(const ViewMsg_Print_Params& print_params,
                              WebFrame* frame,
                              WebView* web_view)
      : frame_(frame),
        web_view_(web_view),
        expected_pages_count_(0) {
    print_canvas_size_.set_width(
        printing::ConvertUnit(print_params.printable_size.width(),
        static_cast<int>(print_params.dpi),
        print_params.desired_dpi));
    print_canvas_size_.set_height(
        printing::ConvertUnit(print_params.printable_size.height(),
        static_cast<int>(print_params.dpi),
        print_params.desired_dpi));

    // Layout page according to printer page size. Since WebKit shrinks the
    // size of the page automatically (from 125% to 200%) we trick it to
    // think the page is 125% larger so the size of the page is correct for
    // minimum (default) scaling.
    // This is important for sites that try to fill the page.
    gfx::Size print_layout_size(print_canvas_size_);
    print_layout_size.set_height(static_cast<int>(
        static_cast<double>(print_layout_size.height()) * 1.25));

    prev_view_size_ = web_view->GetSize();

    web_view->Resize(print_layout_size);

    frame->BeginPrint(print_canvas_size_, &expected_pages_count_);
  }

  int GetExpectedPageCount() const {
    return expected_pages_count_;
  }

  gfx::Size GetPrintCanvasSize() const {
    return print_canvas_size_;
  }

  ~PrepareFrameAndViewForPrint() {
    frame_->EndPrint();
    web_view_->Resize(prev_view_size_);
  }

 private:
  WebFrame* frame_;
  WebView* web_view_;
  gfx::Size print_canvas_size_;
  gfx::Size prev_view_size_;
  int expected_pages_count_;

  DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint);
};

}  // namespace

void PrintWebViewHelper::SyncPrint(WebFrame* frame) {
#if defined(OS_WIN)
  // Retrieve the default print settings to calculate the expected number of
  // pages.
  ViewMsg_Print_Params default_settings;

  IPC::SyncMessage* msg =
      new ViewHostMsg_GetDefaultPrintSettings(routing_id(), &default_settings);
  if (Send(msg)) {
    msg = NULL;
    // Check if the printer returned any settings, if the settings is empty, we
    // can safely assume there are no printer drivers configured. So we safely
    // terminate.
    if (default_settings.IsEmpty()) {
      RunJavaScriptAlert(frame,
          l10n_util::GetString(IDS_DEFAULT_PRINTER_NOT_FOUND_WARNING_TITLE));
      return;
    }

    // Continue only if the settings are valid.
    if (default_settings.dpi && default_settings.document_cookie) {
      int expected_pages_count = 0;

      // Prepare once to calculate the estimated page count.  This must be in
      // a scope for itself (see comments on PrepareFrameAndViewForPrint).
      {
        PrepareFrameAndViewForPrint prep_frame_view(default_settings,
                                                    frame,
                                                    frame->GetView());
        expected_pages_count = prep_frame_view.GetExpectedPageCount();
        DCHECK(expected_pages_count);
      }

      // Ask the browser to show UI to retrieve the final print settings.
      ViewMsg_PrintPages_Params print_settings;
      // host_window_ may be NULL at this point if the current window is a popup
      // and the print() command has been issued from the parent. The receiver
      // of this message has to deal with this.
      msg = new ViewHostMsg_ScriptedPrint(routing_id(),
                                          render_view_->host_window(),
                                          default_settings.document_cookie,
                                          expected_pages_count,
                                          false,  // has_selection
                                          &print_settings);
      if (Send(msg)) {
        msg = NULL;

        // Printing selection only not supported yet.
        if (print_settings.params.selection_only) {
          NOTIMPLEMENTED();
        }

        // If the settings are invalid, early quit.
        if (print_settings.params.dpi &&
            print_settings.params.document_cookie) {
          // Render the printed pages. It will implicitly revert the document to
          // display CSS media type.
          PrintPages(print_settings, frame);
          // All went well.
          return;
        } else {
          // The user cancelled.
        }
      } else {
        // Send() failed.
        NOTREACHED();
      }
    } else {
      // The user cancelled.
    }
  } else {
    // Send() failed.
    NOTREACHED();
  }
  // TODO(maruel):  bug 1123882 Alert the user that printing failed.
#else  // defined(OS_WIN)
  // TODO(port): print not implemented
  NOTIMPLEMENTED();
#endif
}

void PrintWebViewHelper::PrintPages(const ViewMsg_PrintPages_Params& params,
                                 WebFrame* frame) {
  PrepareFrameAndViewForPrint prep_frame_view(params.params,
                                              frame,
                                              frame->GetView());
  int page_count = prep_frame_view.GetExpectedPageCount();

  Send(new ViewHostMsg_DidGetPrintedPagesCount(routing_id(),
                                               params.params.document_cookie,
                                               page_count));
  if (page_count) {
    ViewMsg_PrintPage_Params page_params;
    page_params.params = params.params;
    if (params.pages.empty()) {
      for (int i = 0; i < page_count; ++i) {
        page_params.page_number = i;
        PrintPage(page_params, prep_frame_view.GetPrintCanvasSize(), frame);
      }
    } else {
      for (size_t i = 0; i < params.pages.size(); ++i) {
        page_params.page_number = params.pages[i];
        PrintPage(page_params, prep_frame_view.GetPrintCanvasSize(), frame);
      }
    }
  }
}

void PrintWebViewHelper::PrintPage(const ViewMsg_PrintPage_Params& params,
                                const gfx::Size& canvas_size,
                                WebFrame* frame) {
#if defined(OS_WIN)
  // Generate a memory-based EMF file. The EMF will use the current screen's
  // DPI.
  gfx::Emf emf;

  emf.CreateDc(NULL, NULL);
  HDC hdc = emf.hdc();
  DCHECK(hdc);
  skia::PlatformDeviceWin::InitializeDC(hdc);
  // Since WebKit extends the page width depending on the magical shrink
  // factor we make sure the canvas covers the worst case scenario
  // (x2.0 currently).  PrintContext will then set the correct clipping region.
  int size_x = static_cast<int>(canvas_size.width() * params.params.max_shrink);
  int size_y = static_cast<int>(canvas_size.height() *
      params.params.max_shrink);
  // Calculate the dpi adjustment.
  float shrink = static_cast<float>(canvas_size.width()) /
      params.params.printable_size.width();
#if 0
  // TODO(maruel): This code is kept for testing until the 100% GDI drawing
  // code is stable. maruels use this code's output as a reference when the
  // GDI drawing code fails.

  // Mix of Skia and GDI based.
  skia::PlatformCanvasWin canvas(size_x, size_y, true);
  canvas.drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
  float webkit_shrink = frame->PrintPage(params.page_number, &canvas);
  if (shrink <= 0) {
    NOTREACHED() << "Printing page " << params.page_number << " failed.";
  } else {
    // Update the dpi adjustment with the "page shrink" calculated in webkit.
    shrink /= webkit_shrink;
  }

  // Create a BMP v4 header that we can serialize.
  BITMAPV4HEADER bitmap_header;
  gfx::CreateBitmapV4Header(size_x, size_y, &bitmap_header);
  const SkBitmap& src_bmp = canvas.getDevice()->accessBitmap(true);
  SkAutoLockPixels src_lock(src_bmp);
  int retval = StretchDIBits(hdc,
                             0,
                             0,
                             size_x, size_y,
                             0, 0,
                             size_x, size_y,
                             src_bmp.getPixels(),
                             reinterpret_cast<BITMAPINFO*>(&bitmap_header),
                             DIB_RGB_COLORS,
                             SRCCOPY);
  DCHECK(retval != GDI_ERROR);
#else
  // 100% GDI based.
  skia::VectorCanvas canvas(hdc, size_x, size_y);
  float webkit_shrink = frame->PrintPage(params.page_number, &canvas);
  if (shrink <= 0) {
    NOTREACHED() << "Printing page " << params.page_number << " failed.";
  } else {
    // Update the dpi adjustment with the "page shrink" calculated in webkit.
    shrink /= webkit_shrink;
  }
#endif

  // Done printing. Close the device context to retrieve the compiled EMF.
  if (!emf.CloseDc()) {
    NOTREACHED() << "EMF failed";
  }

  // Get the size of the compiled EMF.
  unsigned buf_size = emf.GetDataSize();
  DCHECK_GT(buf_size, 128u);
  ViewHostMsg_DidPrintPage_Params page_params;
  page_params.data_size = 0;
  page_params.emf_data_handle = NULL;
  page_params.page_number = params.page_number;
  page_params.document_cookie = params.params.document_cookie;
  page_params.actual_shrink = shrink;
  base::SharedMemory shared_buf;

  // http://msdn2.microsoft.com/en-us/library/ms535522.aspx
  // Windows 2000/XP: When a page in a spooled file exceeds approximately 350
  // MB, it can fail to print and not send an error message.
  if (buf_size < 350*1024*1024) {
    // Allocate a shared memory buffer to hold the generated EMF data.
    if (shared_buf.Create(L"", false, false, buf_size) &&
        shared_buf.Map(buf_size)) {
      // Copy the bits into shared memory.
      if (emf.GetData(shared_buf.memory(), buf_size)) {
        page_params.emf_data_handle = shared_buf.handle();
        page_params.data_size = buf_size;
      } else {
        NOTREACHED() << "GetData() failed";
      }
      shared_buf.Unmap();
    } else {
      NOTREACHED() << "Buffer allocation failed";
    }
  } else {
    NOTREACHED() << "Buffer too large: " << buf_size;
  }
  emf.CloseEmf();
  if (Send(new ViewHostMsg_DuplicateSection(routing_id(),
                                            page_params.emf_data_handle,
                                            &page_params.emf_data_handle))) {
    Send(new ViewHostMsg_DidPrintPage(routing_id(), page_params));
  }
#else  // defined(OS_WIN)
  // TODO(port) implement printing
  NOTIMPLEMENTED();
#endif
}

bool PrintWebViewHelper::Send(IPC::Message* msg) {
  return render_view_->Send(msg);
}

int32 PrintWebViewHelper::routing_id() {
  return render_view_->routing_id();
}

void PrintWebViewHelper::GetWindowRect(WebWidget* webwidget,
                                    WebKit::WebRect* rect) {
  NOTREACHED();
}

void PrintWebViewHelper::DidStopLoading(WebView* webview) {
  NOTREACHED();
}

WebKit::WebScreenInfo PrintWebViewHelper::GetScreenInfo(WebWidget* webwidget) {
  WebKit::WebScreenInfo info;
  NOTREACHED();
  return info;
}

gfx::NativeViewId PrintWebViewHelper::GetContainingView(WebWidget* webwidget) {
  NOTREACHED();
  return gfx::NativeViewId();
}

bool PrintWebViewHelper::IsHidden(WebWidget* webwidget) {
  NOTREACHED();
  return true;
}
<|endoftext|>


<commit_before>/************************************************
 *  static_vector_test.hpp
 *  bptree
 *
 *  Copyright (c) 2017, Chi-En Wu
 *  Distributed under MIT License
 ************************************************/

#include <cassert>
#include <cstddef>

#include <array>
#include <stdexcept>

#include <gtest/gtest.h>

#include <bptree/internal/static_vector.hpp>

using bptree::internal::static_vector;

enum class constructed_with {
    default_ctor,
    copy_ctor,
    move_ctor,
    value_ctor
};

class custom_type {
 public:  // Public Method(s)
    custom_type()
      : custom_type(constructed_with::default_ctor, 0)
        { /* do nothing */ }

    custom_type(custom_type const& other)
      : custom_type(constructed_with::copy_ctor, other.val_)
        { /* do nothing */ }

    custom_type(custom_type&& other)
      : custom_type(constructed_with::move_ctor, other.val_)
        { other.val_ = 0; }

    explicit custom_type(int n)
      : custom_type(constructed_with::value_ctor, n)
        { /* do nothing */ }

    // used only for generating expected results in tests
    custom_type(constructed_with ctor, int n)
      : ctor_(ctor), val_(n)
        { ++num_instances_; }

    ~custom_type()
        { --num_instances_; }

    custom_type& operator=(custom_type const& other)
        { val_ = other.val_; return *this; }

    custom_type& operator=(custom_type&& other)
        { val_ = other.val_; other.val_ = 0; return *this; }

    bool operator==(custom_type const& other) const
        { return val_ == other.val_ && ctor_ == other.ctor_; }

    int get() const
        { return val_; }

    constructed_with ctor() const
        { return ctor_; }

 public:  // Public Static Method(s)
    static void reset_num_instances()
        { num_instances_ = 0; }

    static std::size_t num_instances()
        { return num_instances_; }

 private:  // Private Property(ies)
    int val_;
    constructed_with ctor_;

 private:  // Private Static Property(ies)
    static std::size_t num_instances_;
};

class StaticVectorTest : public ::testing::Test {
 protected:
     virtual void SetUp() {
         custom_type::reset_num_instances();
     }
};

std::size_t custom_type::num_instances_ = 0;
std::size_t const SIZE_VECTOR = 10;

template <typename T, std::size_t N>
void assert_static_vector_size(static_vector<T, N> const& v, std::size_t size) {
    assert(size <= N);

    EXPECT_EQ(size == 0, v.empty());
    EXPECT_EQ(size == N, v.full());
    EXPECT_EQ(size, v.size());
    EXPECT_EQ(N, v.max_size());
    EXPECT_EQ(N, v.capacity());

    EXPECT_THROW(v.at(size), std::out_of_range);
}

template <typename T, std::size_t N, typename F>
void assert_static_vector_values(static_vector<T, N> const& v, std::size_t count,
                                 F get_expected_value) {
    assert_static_vector_size(v, count);

    typename static_vector<T, N>::const_pointer data_ptr = v.data();
    for (std::size_t pos = 0; pos < count; ++pos, ++data_ptr) {
        T const& expected = get_expected_value(pos);
        EXPECT_EQ(expected, v[pos]);
        EXPECT_EQ(expected, v.at(pos));
        EXPECT_EQ(expected, *data_ptr);
    }
}

TEST_F(StaticVectorTest, EmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v;

    EXPECT_EQ(0, custom_type::num_instances());

    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, ConstructWithCount) {
    std::size_t const COUNT = 3;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT);

    EXPECT_EQ(COUNT, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::default_ctor, 0);
    auto get_expected_value = [&expected_value](std::size_t pos) -> decltype(auto)
        { return (expected_value); };
    assert_static_vector_values(v, COUNT, get_expected_value);
}

TEST_F(StaticVectorTest, ConstructEmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v(0);

    EXPECT_EQ(0, custom_type::num_instances());

    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, ConstructFullVector) {
    static_vector<custom_type, SIZE_VECTOR> v(SIZE_VECTOR);

    EXPECT_EQ(SIZE_VECTOR, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::default_ctor, 0);
    auto get_expected_value = [&expected_value](std::size_t pos) -> decltype(auto)
        { return (expected_value); };
    assert_static_vector_values(v, SIZE_VECTOR, get_expected_value);
}

TEST_F(StaticVectorTest, ConstructWithCountAndValue) {
    std::size_t const COUNT = 3;
    int const VALUE = 10;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT, custom_type(VALUE));

    EXPECT_EQ(COUNT, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::copy_ctor, VALUE);
    auto get_expected_value = [&expected_value](std::size_t pos) -> decltype(auto)
        { return (expected_value); };
    assert_static_vector_values(v, COUNT, get_expected_value);
}

TEST_F(StaticVectorTest, ConstructWithIteratorPair) {
    std::array<custom_type, 5> arr = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    static_vector<custom_type, SIZE_VECTOR> v(arr.begin(), arr.end());

    EXPECT_EQ(arr.size() * 2, custom_type::num_instances());

    auto get_expected_value = [&arr](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, arr[pos].get()); };
    assert_static_vector_values(v, arr.size(), get_expected_value);
}

TEST_F(StaticVectorTest, ConstructWithInitializerList) {
    std::size_t const SIZE = 5;
    static_vector<custom_type, SIZE_VECTOR> v = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    EXPECT_EQ(SIZE, custom_type::num_instances());

    int expected_values[] = {1, 2, 3, 5, 8};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v, SIZE, get_expected_value);
}

TEST_F(StaticVectorTest, ConstructWithAnotherVector) {
    std::size_t const SIZE = 5;
    static_vector<custom_type, SIZE_VECTOR> v1 = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    static_vector<custom_type, SIZE_VECTOR> v2 = v1;

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());

    int expected_values[] = {1, 2, 3, 5, 8};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v2, SIZE, get_expected_value);
}

TEST_F(StaticVectorTest, DestructValues) {
    {
        static_vector<custom_type, SIZE_VECTOR> v(3);
        EXPECT_EQ(3, custom_type::num_instances());
    }

    EXPECT_EQ(0, custom_type::num_instances());
}

TEST_F(StaticVectorTest, ClearValues) {
    static_vector<custom_type, SIZE_VECTOR> v = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    v.clear();

    EXPECT_EQ(0, custom_type::num_instances());
    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, AssignWithOperatorAndInitializerList) {
    static_vector<custom_type, SIZE_VECTOR> v = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    std::size_t const SIZE = 3;
    v = {
        custom_type(13),
        custom_type(21),
        custom_type(34)
    };

    EXPECT_EQ(SIZE, custom_type::num_instances());

    int expected_values[] = {13, 21, 34};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v, SIZE, get_expected_value);
}

TEST_F(StaticVectorTest, AssignWithOperatorAndAnotherVector) {
    static_vector<custom_type, SIZE_VECTOR> v1 = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    std::size_t const SIZE = 3;
    static_vector<custom_type, SIZE_VECTOR> v2 = {
        custom_type(13),
        custom_type(21),
        custom_type(34)
    };

    v1 = v2;

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());

    int expected_values[] = {13, 21, 34};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v1, SIZE, get_expected_value);
}
<commit_msg>:hammer: Generate data for test cases of `static_vector` with macro<commit_after>/************************************************
 *  static_vector_test.hpp
 *  bptree
 *
 *  Copyright (c) 2017, Chi-En Wu
 *  Distributed under MIT License
 ************************************************/

#include <cassert>
#include <cstddef>

#include <array>
#include <stdexcept>

#include <gtest/gtest.h>

#include <bptree/internal/static_vector.hpp>

using bptree::internal::static_vector;

enum class constructed_with {
    default_ctor,
    copy_ctor,
    move_ctor,
    value_ctor
};

class custom_type {
 public:  // Public Method(s)
    custom_type()
      : custom_type(constructed_with::default_ctor, 0)
        { /* do nothing */ }

    custom_type(custom_type const& other)
      : custom_type(constructed_with::copy_ctor, other.val_)
        { /* do nothing */ }

    custom_type(custom_type&& other)
      : custom_type(constructed_with::move_ctor, other.val_)
        { other.val_ = 0; }

    explicit custom_type(int n)
      : custom_type(constructed_with::value_ctor, n)
        { /* do nothing */ }

    // used only for generating expected results in tests
    custom_type(constructed_with ctor, int n)
      : ctor_(ctor), val_(n)
        { ++num_instances_; }

    ~custom_type()
        { --num_instances_; }

    custom_type& operator=(custom_type const& other)
        { val_ = other.val_; return *this; }

    custom_type& operator=(custom_type&& other)
        { val_ = other.val_; other.val_ = 0; return *this; }

    bool operator==(custom_type const& other) const
        { return val_ == other.val_ && ctor_ == other.ctor_; }

    int get() const
        { return val_; }

    constructed_with ctor() const
        { return ctor_; }

 public:  // Public Static Method(s)
    static void reset_num_instances()
        { num_instances_ = 0; }

    static std::size_t num_instances()
        { return num_instances_; }

 private:  // Private Property(ies)
    int val_;
    constructed_with ctor_;

 private:  // Private Static Property(ies)
    static std::size_t num_instances_;
};

class StaticVectorTest : public ::testing::Test {
 protected:
     virtual void SetUp() {
         custom_type::reset_num_instances();
     }
};

std::size_t custom_type::num_instances_ = 0;
std::size_t const SIZE_VECTOR = 10;

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, n, ...) n
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

#define INIT_LIST_IMPL_1(wrap, value) wrap(value)
#define INIT_LIST_IMPL_2(wrap, _1, value) \
        INIT_LIST_IMPL_1(wrap, _1), wrap(value)
#define INIT_LIST_IMPL_3(wrap, _1, _2, value) \
        INIT_LIST_IMPL_2(wrap, _1, _2), wrap(value)
#define INIT_LIST_IMPL_4(wrap, _1, _2, _3, value) \
        INIT_LIST_IMPL_3(wrap, _1, _2, _3), wrap(value)
#define INIT_LIST_IMPL_5(wrap, _1, _2, _3, _4, value) \
        INIT_LIST_IMPL_4(wrap, _1, _2, _3, _4), wrap(value)
#define INIT_LIST_IMPL_6(wrap, _1, _2, _3, _4, _5, value) \
        INIT_LIST_IMPL_5(wrap, _1, _2, _3, _4, _5), wrap(value)
#define INIT_LIST_IMPL_7(wrap, _1, _2, _3, _4, _5, _6, value) \
        INIT_LIST_IMPL_6(wrap, _1, _2, _3, _4, _5, _6), wrap(value)
#define INIT_LIST_IMPL_8(wrap, _1, _2, _3, _4, _5, _6, _7, value) \
        INIT_LIST_IMPL_7(wrap, _1, _2, _3, _4, _5, _6, _7), wrap(value)
#define INIT_LIST_IMPL_9(wrap, _1, _2, _3, _4, _5, _6, _7, _8, value) \
        INIT_LIST_IMPL_8(wrap, _1, _2, _3, _4, _5, _6, _7, _8), wrap(value)
#define INIT_LIST_IMPL_10(wrap, _1, _2, _3, _4, _5, _6, _7, _8, _9, value) \
        INIT_LIST_IMPL_9(wrap, _1, _2, _3, _4, _5, _6, _7, _8, _9), wrap(value)

#define INIT_LIST_IMPL_N(n, wrap, ...)  INIT_LIST_IMPL_##n(wrap, __VA_ARGS__)
#define INIT_LIST_IMPL(n, wrap, ...)    INIT_LIST_IMPL_N(n, wrap, __VA_ARGS__)

#define INIT_LIST(wrap, ...)            { INIT_LIST_IMPL(VA_NARGS(__VA_ARGS__), wrap, __VA_ARGS__) }

#define TEST_VALUES                     1, 2, 3, 5, 8
#define EXTRA_TEST_VALUES               13, 21, 34

template <typename T, std::size_t N>
void assert_static_vector_size(static_vector<T, N> const& v, std::size_t size) {
    assert(size <= N);

    EXPECT_EQ(size == 0, v.empty());
    EXPECT_EQ(size == N, v.full());
    EXPECT_EQ(size, v.size());
    EXPECT_EQ(N, v.max_size());
    EXPECT_EQ(N, v.capacity());

    EXPECT_THROW(v.at(size), std::out_of_range);
}

template <typename T, std::size_t N, typename F>
void assert_static_vector_values(static_vector<T, N> const& v, std::size_t count,
                                 F get_expected_value) {
    assert_static_vector_size(v, count);

    typename static_vector<T, N>::const_pointer data_ptr = v.data();
    for (std::size_t pos = 0; pos < count; ++pos, ++data_ptr) {
        T const& expected = get_expected_value(pos);
        EXPECT_EQ(expected, v[pos]);
        EXPECT_EQ(expected, v.at(pos));
        EXPECT_EQ(expected, *data_ptr);
    }
}

TEST_F(StaticVectorTest, EmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v;

    EXPECT_EQ(0, custom_type::num_instances());

    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, ConstructWithCount) {
    std::size_t const COUNT = 3;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT);

    EXPECT_EQ(COUNT, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::default_ctor, 0);
    auto get_expected_value = [&expected_value](std::size_t pos) -> decltype(auto)
        { return (expected_value); };
    assert_static_vector_values(v, COUNT, get_expected_value);
}

TEST_F(StaticVectorTest, ConstructEmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v(0);

    EXPECT_EQ(0, custom_type::num_instances());

    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, ConstructFullVector) {
    static_vector<custom_type, SIZE_VECTOR> v(SIZE_VECTOR);

    EXPECT_EQ(SIZE_VECTOR, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::default_ctor, 0);
    auto get_expected_value = [&expected_value](std::size_t pos) -> decltype(auto)
        { return (expected_value); };
    assert_static_vector_values(v, SIZE_VECTOR, get_expected_value);
}

TEST_F(StaticVectorTest, ConstructWithCountAndValue) {
    std::size_t const COUNT = 3;
    int const VALUE = 10;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT, custom_type(VALUE));

    EXPECT_EQ(COUNT, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::copy_ctor, VALUE);
    auto get_expected_value = [&expected_value](std::size_t pos) -> decltype(auto)
        { return (expected_value); };
    assert_static_vector_values(v, COUNT, get_expected_value);
}

TEST_F(StaticVectorTest, ConstructWithIteratorPair) {
    std::size_t constexpr SIZE = VA_NARGS(TEST_VALUES);
    std::array<custom_type, SIZE> arr = INIT_LIST(custom_type, TEST_VALUES);

    static_vector<custom_type, SIZE_VECTOR> v(arr.begin(), arr.end());

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());

    auto get_expected_value = [&arr](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, arr[pos].get()); };
    assert_static_vector_values(v, SIZE, get_expected_value);
}

TEST_F(StaticVectorTest, ConstructWithInitializerList) {
    std::size_t const SIZE = VA_NARGS(TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v = INIT_LIST(custom_type, TEST_VALUES);

    EXPECT_EQ(SIZE, custom_type::num_instances());

    int expected_values[] = {TEST_VALUES};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v, SIZE, get_expected_value);
}

TEST_F(StaticVectorTest, ConstructWithAnotherVector) {
    std::size_t const SIZE = VA_NARGS(TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v1 = INIT_LIST(custom_type, TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v2 = v1;

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());

    int expected_values[] = {TEST_VALUES};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v2, SIZE, get_expected_value);
}

TEST_F(StaticVectorTest, DestructValues) {
    {
        static_vector<custom_type, SIZE_VECTOR> v(3);
        EXPECT_EQ(3, custom_type::num_instances());
    }

    EXPECT_EQ(0, custom_type::num_instances());
}

TEST_F(StaticVectorTest, ClearValues) {
    static_vector<custom_type, SIZE_VECTOR> v = INIT_LIST(custom_type, TEST_VALUES);

    v.clear();

    EXPECT_EQ(0, custom_type::num_instances());
    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, AssignWithOperatorAndInitializerList) {
    std::size_t const SIZE = VA_NARGS(EXTRA_TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v = INIT_LIST(custom_type, TEST_VALUES);
    v = INIT_LIST(custom_type, EXTRA_TEST_VALUES);

    EXPECT_EQ(SIZE, custom_type::num_instances());

    int expected_values[] = {EXTRA_TEST_VALUES};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v, SIZE, get_expected_value);
}

TEST_F(StaticVectorTest, AssignWithOperatorAndAnotherVector) {
    std::size_t const SIZE = VA_NARGS(EXTRA_TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v1 = INIT_LIST(custom_type, TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v2 = INIT_LIST(custom_type, EXTRA_TEST_VALUES);
    v1 = v2;

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());

    int expected_values[] = {EXTRA_TEST_VALUES};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v1, SIZE, get_expected_value);
}
<|endoftext|>


<commit_before>//===---------------------------- StackMaps.cpp ---------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "stackmaps"

#include "llvm/CodeGen/StackMaps.h"

#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetOpcodes.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetRegisterInfo.h"

#include <iterator>

using namespace llvm;

void StackMaps::recordStackMap(const MachineInstr &MI, uint32_t ID,
                               MachineInstr::const_mop_iterator MOI,
                               MachineInstr::const_mop_iterator MOE) {

  MCContext &OutContext = AP.OutStreamer.getContext();
  MCSymbol *MILabel = OutContext.CreateTempSymbol();
  AP.OutStreamer.EmitLabel(MILabel);

  LocationVec CallsiteLocs;

  while (MOI != MOE) {
    std::pair<Location, MachineInstr::const_mop_iterator> ParseResult =
      OpParser(MOI, MOE);

    Location &Loc = ParseResult.first;

    // Move large constants into the constant pool.
    if (Loc.LocType == Location::Constant && (Loc.Offset & ~0xFFFFFFFFULL)) {
      Loc.LocType = Location::ConstantIndex;
      Loc.Offset = ConstPool.getConstantIndex(Loc.Offset);
    }

    CallsiteLocs.push_back(Loc);
    MOI = ParseResult.second;
  }

  const MCExpr *CSOffsetExpr = MCBinaryExpr::CreateSub(
    MCSymbolRefExpr::Create(MILabel, OutContext),
    MCSymbolRefExpr::Create(AP.CurrentFnSym, OutContext),
    OutContext);

  CSInfos.push_back(CallsiteInfo(CSOffsetExpr, ID, CallsiteLocs));
}

/// serializeToStackMapSection conceptually populates the following fields:
///
/// uint32 : Reserved (header)
/// uint32 : NumConstants
/// int64  : Constants[NumConstants]
/// uint32 : NumRecords
/// StkMapRecord[NumRecords] {
///   uint32 : PatchPoint ID
///   uint32 : Instruction Offset
///   uint16 : Reserved (record flags)
///   uint16 : NumLocations
///   Location[NumLocations] {
///     uint8  : Register | Direct | Indirect | Constant | ConstantIndex
///     uint8  : Reserved (location flags)
///     uint16 : Dwarf RegNum
///     int32  : Offset
///   }
/// }
///
/// Location Encoding, Type, Value:
///   0x1, Register, Reg                 (value in register)
///   0x2, Direct, Reg + Offset          (frame index)
///   0x3, Indirect, [Reg + Offset]      (spilled value)
///   0x4, Constant, Offset              (small constant)
///   0x5, ConstIndex, Constants[Offset] (large constant)
///
void StackMaps::serializeToStackMapSection() {
  // Bail out if there's no stack map data.
  if (CSInfos.empty())
    return;

  MCContext &OutContext = AP.OutStreamer.getContext();
  const TargetRegisterInfo *TRI = AP.TM.getRegisterInfo();

  // Create the section.
  const MCSection *StackMapSection =
    OutContext.getObjectFileInfo().getStackMapSection();
  AP.OutStreamer.SwitchSection(StackMapSection);

  // Emit a dummy symbol to force section inclusion.
  AP.OutStreamer.EmitLabel(
    OutContext.GetOrCreateSymbol(Twine("__LLVM_StackMaps")));

  // Serialize data.
  const char *WSMP = "Stack Maps: ";
  (void)WSMP;
  const MCRegisterInfo &MCRI = *OutContext.getRegisterInfo();

  DEBUG(dbgs() << "********** Stack Map Output **********\n");

  // Header.
  AP.OutStreamer.EmitIntValue(0, 4);

  // Num constants.
  AP.OutStreamer.EmitIntValue(ConstPool.getNumConstants(), 4);

  // Constant pool entries.
  for (unsigned i = 0; i < ConstPool.getNumConstants(); ++i)
    AP.OutStreamer.EmitIntValue(ConstPool.getConstant(i), 8);

  DEBUG(dbgs() << WSMP << "#callsites = " << CSInfos.size() << "\n");
  AP.OutStreamer.EmitIntValue(CSInfos.size(), 4);

  for (CallsiteInfoList::const_iterator CSII = CSInfos.begin(),
                                        CSIE = CSInfos.end();
       CSII != CSIE; ++CSII) {

    unsigned CallsiteID = CSII->ID;
    const LocationVec &CSLocs = CSII->Locations;

    DEBUG(dbgs() << WSMP << "callsite " << CallsiteID << "\n");

    // Verify stack map entry. It's better to communicate a problem to the
    // runtime than crash in case of in-process compilation. Currently, we do
    // simple overflow checks, but we may eventually communicate other
    // compilation errors this way.
    if (CSLocs.size() > UINT16_MAX) {
      AP.OutStreamer.EmitIntValue(UINT32_MAX, 4); // Invalid ID.
      AP.OutStreamer.EmitValue(CSII->CSOffsetExpr, 4);
      AP.OutStreamer.EmitIntValue(0, 2); // Reserved.
      AP.OutStreamer.EmitIntValue(0, 2); // 0 locations.
      continue;
    }

    AP.OutStreamer.EmitIntValue(CallsiteID, 4);
    AP.OutStreamer.EmitValue(CSII->CSOffsetExpr, 4);

    // Reserved for flags.
    AP.OutStreamer.EmitIntValue(0, 2);

    DEBUG(dbgs() << WSMP << "  has " << CSLocs.size() << " locations\n");

    AP.OutStreamer.EmitIntValue(CSLocs.size(), 2);

    unsigned operIdx = 0;
    for (LocationVec::const_iterator LocI = CSLocs.begin(), LocE = CSLocs.end();
         LocI != LocE; ++LocI, ++operIdx) {
      const Location &Loc = *LocI;
      DEBUG(
        dbgs() << WSMP << "  Loc " << operIdx << ": ";
        switch (Loc.LocType) {
        case Location::Unprocessed:
          dbgs() << "<Unprocessed operand>";
          break;
        case Location::Register:
          dbgs() << "Register " << MCRI.getName(Loc.Reg);
          break;
        case Location::Direct:
          dbgs() << "Direct " << MCRI.getName(Loc.Reg);
          if (Loc.Offset)
            dbgs() << " + " << Loc.Offset;
          break;
        case Location::Indirect:
          dbgs() << "Indirect " << MCRI.getName(Loc.Reg)
                 << " + " << Loc.Offset;
          break;
        case Location::Constant:
          dbgs() << "Constant " << Loc.Offset;
          break;
        case Location::ConstantIndex:
          dbgs() << "Constant Index " << Loc.Offset;
          break;
        }
        dbgs() << "\n";
      );

      unsigned RegNo = 0;
      if(Loc.Reg) {
        RegNo = MCRI.getDwarfRegNum(Loc.Reg, false);
        for (MCSuperRegIterator SR(Loc.Reg, TRI);
             SR.isValid() && (int)RegNo < 0; ++SR) {
          RegNo = TRI->getDwarfRegNum(*SR, false);
        }
      }
      else {
        assert((Loc.LocType != Location::Register
                && Loc.LocType != Location::Register) &&
               "Missing location register");
      }
      AP.OutStreamer.EmitIntValue(Loc.LocType, 1);
      AP.OutStreamer.EmitIntValue(0, 1); // Reserved location flags.
      AP.OutStreamer.EmitIntValue(RegNo, 2);
      AP.OutStreamer.EmitIntValue(Loc.Offset, 4);
    }
  }

  AP.OutStreamer.AddBlankLine();

  CSInfos.clear();
}
<commit_msg>Fix some minor issues with r194282 to get the tree healthy again.<commit_after>//===---------------------------- StackMaps.cpp ---------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "stackmaps"

#include "llvm/CodeGen/StackMaps.h"

#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetOpcodes.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetRegisterInfo.h"

#include <iterator>

using namespace llvm;

void StackMaps::recordStackMap(const MachineInstr &MI, uint32_t ID,
                               MachineInstr::const_mop_iterator MOI,
                               MachineInstr::const_mop_iterator MOE) {

  MCContext &OutContext = AP.OutStreamer.getContext();
  MCSymbol *MILabel = OutContext.CreateTempSymbol();
  AP.OutStreamer.EmitLabel(MILabel);

  LocationVec CallsiteLocs;

  while (MOI != MOE) {
    std::pair<Location, MachineInstr::const_mop_iterator> ParseResult =
      OpParser(MOI, MOE);

    Location &Loc = ParseResult.first;

    // Move large constants into the constant pool.
    if (Loc.LocType == Location::Constant && (Loc.Offset & ~0xFFFFFFFFULL)) {
      Loc.LocType = Location::ConstantIndex;
      Loc.Offset = ConstPool.getConstantIndex(Loc.Offset);
    }

    CallsiteLocs.push_back(Loc);
    MOI = ParseResult.second;
  }

  const MCExpr *CSOffsetExpr = MCBinaryExpr::CreateSub(
    MCSymbolRefExpr::Create(MILabel, OutContext),
    MCSymbolRefExpr::Create(AP.CurrentFnSym, OutContext),
    OutContext);

  CSInfos.push_back(CallsiteInfo(CSOffsetExpr, ID, CallsiteLocs));
}

/// serializeToStackMapSection conceptually populates the following fields:
///
/// uint32 : Reserved (header)
/// uint32 : NumConstants
/// int64  : Constants[NumConstants]
/// uint32 : NumRecords
/// StkMapRecord[NumRecords] {
///   uint32 : PatchPoint ID
///   uint32 : Instruction Offset
///   uint16 : Reserved (record flags)
///   uint16 : NumLocations
///   Location[NumLocations] {
///     uint8  : Register | Direct | Indirect | Constant | ConstantIndex
///     uint8  : Reserved (location flags)
///     uint16 : Dwarf RegNum
///     int32  : Offset
///   }
/// }
///
/// Location Encoding, Type, Value:
///   0x1, Register, Reg                 (value in register)
///   0x2, Direct, Reg + Offset          (frame index)
///   0x3, Indirect, [Reg + Offset]      (spilled value)
///   0x4, Constant, Offset              (small constant)
///   0x5, ConstIndex, Constants[Offset] (large constant)
///
void StackMaps::serializeToStackMapSection() {
  // Bail out if there's no stack map data.
  if (CSInfos.empty())
    return;

  MCContext &OutContext = AP.OutStreamer.getContext();
  const TargetRegisterInfo *TRI = AP.TM.getRegisterInfo();

  // Create the section.
  const MCSection *StackMapSection =
    OutContext.getObjectFileInfo()->getStackMapSection();
  AP.OutStreamer.SwitchSection(StackMapSection);

  // Emit a dummy symbol to force section inclusion.
  AP.OutStreamer.EmitLabel(
    OutContext.GetOrCreateSymbol(Twine("__LLVM_StackMaps")));

  // Serialize data.
  const char *WSMP = "Stack Maps: ";
  (void)WSMP;
  const MCRegisterInfo &MCRI = *OutContext.getRegisterInfo();

  DEBUG(dbgs() << "********** Stack Map Output **********\n");

  // Header.
  AP.OutStreamer.EmitIntValue(0, 4);

  // Num constants.
  AP.OutStreamer.EmitIntValue(ConstPool.getNumConstants(), 4);

  // Constant pool entries.
  for (unsigned i = 0; i < ConstPool.getNumConstants(); ++i)
    AP.OutStreamer.EmitIntValue(ConstPool.getConstant(i), 8);

  DEBUG(dbgs() << WSMP << "#callsites = " << CSInfos.size() << "\n");
  AP.OutStreamer.EmitIntValue(CSInfos.size(), 4);

  for (CallsiteInfoList::const_iterator CSII = CSInfos.begin(),
                                        CSIE = CSInfos.end();
       CSII != CSIE; ++CSII) {

    unsigned CallsiteID = CSII->ID;
    const LocationVec &CSLocs = CSII->Locations;

    DEBUG(dbgs() << WSMP << "callsite " << CallsiteID << "\n");

    // Verify stack map entry. It's better to communicate a problem to the
    // runtime than crash in case of in-process compilation. Currently, we do
    // simple overflow checks, but we may eventually communicate other
    // compilation errors this way.
    if (CSLocs.size() > UINT16_MAX) {
      AP.OutStreamer.EmitIntValue(UINT32_MAX, 4); // Invalid ID.
      AP.OutStreamer.EmitValue(CSII->CSOffsetExpr, 4);
      AP.OutStreamer.EmitIntValue(0, 2); // Reserved.
      AP.OutStreamer.EmitIntValue(0, 2); // 0 locations.
      continue;
    }

    AP.OutStreamer.EmitIntValue(CallsiteID, 4);
    AP.OutStreamer.EmitValue(CSII->CSOffsetExpr, 4);

    // Reserved for flags.
    AP.OutStreamer.EmitIntValue(0, 2);

    DEBUG(dbgs() << WSMP << "  has " << CSLocs.size() << " locations\n");

    AP.OutStreamer.EmitIntValue(CSLocs.size(), 2);

    unsigned operIdx = 0;
    for (LocationVec::const_iterator LocI = CSLocs.begin(), LocE = CSLocs.end();
         LocI != LocE; ++LocI, ++operIdx) {
      const Location &Loc = *LocI;
      DEBUG(
        dbgs() << WSMP << "  Loc " << operIdx << ": ";
        switch (Loc.LocType) {
        case Location::Unprocessed:
          dbgs() << "<Unprocessed operand>";
          break;
        case Location::Register:
          dbgs() << "Register " << MCRI.getName(Loc.Reg);
          break;
        case Location::Direct:
          dbgs() << "Direct " << MCRI.getName(Loc.Reg);
          if (Loc.Offset)
            dbgs() << " + " << Loc.Offset;
          break;
        case Location::Indirect:
          dbgs() << "Indirect " << MCRI.getName(Loc.Reg)
                 << " + " << Loc.Offset;
          break;
        case Location::Constant:
          dbgs() << "Constant " << Loc.Offset;
          break;
        case Location::ConstantIndex:
          dbgs() << "Constant Index " << Loc.Offset;
          break;
        }
        dbgs() << "\n";
      );

      unsigned RegNo = 0;
      if(Loc.Reg) {
        RegNo = MCRI.getDwarfRegNum(Loc.Reg, false);
        for (MCSuperRegIterator SR(Loc.Reg, TRI);
             SR.isValid() && (int)RegNo < 0; ++SR) {
          RegNo = TRI->getDwarfRegNum(*SR, false);
        }
      }
      else {
        assert((Loc.LocType != Location::Register
                && Loc.LocType != Location::Register) &&
               "Missing location register");
      }
      AP.OutStreamer.EmitIntValue(Loc.LocType, 1);
      AP.OutStreamer.EmitIntValue(0, 1); // Reserved location flags.
      AP.OutStreamer.EmitIntValue(RegNo, 2);
      AP.OutStreamer.EmitIntValue(Loc.Offset, 4);
    }
  }

  AP.OutStreamer.AddBlankLine();

  CSInfos.clear();
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef ETL_ITERATOR_HPP
#define ETL_ITERATOR_HPP

#include <iterator>

#include "traits_lite.hpp"

namespace etl {

template<typename Expr, bool Ref = false, bool Const = true>
struct iterator : public std::iterator<std::random_access_iterator_tag, value_t<Expr>> {
private:
    Expr* expr;
    std::size_t i;

public:
    using base_iterator_t = std::iterator<std::random_access_iterator_tag, value_t<Expr>>;
    using value_type = value_t<Expr>;
    using reference_t = std::conditional_t<Ref,
          std::conditional_t<Const, const value_type&, value_type&>,
          value_type>;
    using pointer_t = std::conditional_t<Const, const value_type*, value_type*>;
    using difference_t = typename base_iterator_t::difference_type;

    iterator(Expr& expr, std::size_t i) : expr(&expr), i(i) {}

    reference_t operator*(){
        return (*expr)[i];
    }

    reference_t operator[](difference_t n){
        return (*expr)[n];
    }

    pointer_t operator->(){
        return &(*expr)[i];
    }

    iterator& operator--(){
        --i;
        return *this;
    }

    iterator operator--(int){
        iterator prev(*this);
        --i;
        return prev;
    }

    iterator& operator++(){
        ++i;
        return *this;
    }

    iterator operator++(int){
        iterator prev(*this);
        ++i;
        return prev;
    }

    iterator& operator+=(difference_t n){
        i += n;
        return *this;
    }

    iterator& operator-=(difference_t n){
        i -= n;
        return *this;
    }

    iterator operator+(difference_t n){
        iterator it(*this);
        it += n;
        return it;
    }

    iterator operator-(difference_t n){
        iterator it(*this);
        it -= n;
        return it;
    }

    difference_t operator-(const iterator& it){
        return i - it.i;
    }

    bool operator==(const iterator& other) const {
        return expr == other.expr && i == other.i;
    }

    bool operator!=(const iterator& other) const {
        return !(*this == other);
    }
};

} //end of namespace etl

#endif
<commit_msg>Make iterator random-access-iterator compliant<commit_after>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef ETL_ITERATOR_HPP
#define ETL_ITERATOR_HPP

#include <iterator>

#include "traits_lite.hpp"

namespace etl {

template<typename Expr, bool Ref = false, bool Const = true>
struct iterator : public std::iterator<std::random_access_iterator_tag, value_t<Expr>> {
private:
    Expr* expr;
    std::size_t i;

public:
    using base_iterator_t = std::iterator<std::random_access_iterator_tag, value_t<Expr>>;
    using value_type = value_t<Expr>;
    using reference_t = std::conditional_t<Ref,
          std::conditional_t<Const, const value_type&, value_type&>,
          value_type>;
    using pointer_t = std::conditional_t<Const, const value_type*, value_type*>;
    using difference_t = typename base_iterator_t::difference_type;

    iterator(Expr& expr, std::size_t i) : expr(&expr), i(i) {}

    reference_t operator*(){
        return (*expr)[i];
    }

    reference_t operator[](difference_t n){
        return (*expr)[n];
    }

    pointer_t operator->(){
        return &(*expr)[i];
    }

    iterator& operator--(){
        --i;
        return *this;
    }

    iterator operator--(int){
        iterator prev(*this);
        --i;
        return prev;
    }

    iterator& operator++(){
        ++i;
        return *this;
    }

    iterator operator++(int){
        iterator prev(*this);
        ++i;
        return prev;
    }

    iterator& operator+=(difference_t n){
        i += n;
        return *this;
    }

    iterator& operator-=(difference_t n){
        i -= n;
        return *this;
    }

    iterator operator+(difference_t n){
        iterator it(*this);
        it += n;
        return it;
    }

    iterator operator-(difference_t n){
        iterator it(*this);
        it -= n;
        return it;
    }

    difference_t operator-(const iterator& it){
        return i - it.i;
    }

    bool operator==(const iterator& other) const {
        return expr == other.expr && i == other.i;
    }

    bool operator!=(const iterator& other) const {
        return !(*this == other);
    }

    bool operator>(const iterator& other) const {
        return i > other.i;
    }

    bool operator>=(const iterator& other) const {
        return i >= other.i;
    }

    bool operator<(const iterator& other) const {
        return i < other.i;
    }

    bool operator<=(const iterator& other) const {
        return i <= other.i;

    }
};

} //end of namespace etl

#endif
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2020 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

#include <fstream>

#include "cpp_utils/assert.hpp" //Assertions
#include "cpp_utils/io.hpp"     // For binary writing

#include "etl/etl.hpp"

#include "layer.hpp"
#include "layer_traits.hpp"
#include "util/tmp.hpp"

namespace dll {

/*!
 * \brief Base class for RNN layers (fast / dynamic)
 */
template <typename Derived, typename Desc>
struct base_rnn_layer : layer<Derived> {
    using desc      = Desc;                            ///< The descriptor of the layer
    using derived_t = Derived;                         ///< The derived type (CRTP)
    using weight    = typename desc::weight;           ///< The data type for this layer
    using this_type = base_rnn_layer<derived_t, desc>; ///< The type of this layer
    using base_type = layer<Derived>;                  ///< The base type

    static constexpr auto activation_function = desc::activation_function; ///< The layer's activation function

    /*!
     * \brief Initialize the neural layer
     */
    base_rnn_layer()
            : base_type() {
        // Nothing to init here
    }

    base_rnn_layer(const base_rnn_layer& rhs) = delete;
    base_rnn_layer(base_rnn_layer&& rhs)      = delete;

    base_rnn_layer& operator=(const base_rnn_layer& rhs) = delete;
    base_rnn_layer& operator=(base_rnn_layer&& rhs) = delete;

    mutable etl::dyn_matrix<float, 3> x_t;
    mutable etl::dyn_matrix<float, 3> s_t;

    mutable etl::dyn_matrix<float, 3> delta_t;
    mutable etl::dyn_matrix<float, 3> d_h_t;
    mutable etl::dyn_matrix<float, 3> d_x_t;

    void prepare_cache(size_t Batch, size_t time_steps, size_t sequence_length, size_t hidden_units) const {
        if (cpp_unlikely(!x_t.memory_start())) {
            // Forward cache
            x_t.resize(time_steps, Batch, sequence_length);
            s_t.resize(time_steps, Batch, hidden_units);

            // Backward cache
            delta_t.resize(time_steps, Batch, hidden_units);
            d_h_t.resize(time_steps, Batch, hidden_units);
            d_x_t.resize(time_steps, Batch, sequence_length);
        }
    }

    /*!
     * \brief Apply the layer to the given batch of input.
     *
     * \param x A batch of input
     * \param output A batch of output that will be filled
     * \param w The W weights matrix
     * \param u The U weights matrix
     */
    template <typename H, typename V, typename W, typename U, typename B>
    void forward_batch_impl(H&& output, const V& x, const W& w, const U& u, const B& b, size_t time_steps, size_t sequence_length, size_t hidden_units) const {
        const auto Batch = etl::dim<0>(x);

        prepare_cache(Batch, time_steps, sequence_length, hidden_units);

        // 1. Rearrange input
        x_t = transpose_front(x);

        // 2. Forward propagation through time

        // t == 0

        s_t(0) = f_activate<activation_function>(bias_add_2d(x_t(0) * u, b));

        for (size_t t = 1; t < time_steps; ++t) {
            s_t(t) = f_activate<activation_function>(bias_add_2d(x_t(t) * u + s_t(t - 1) * w, b));
        }

        // 3. Rearrange the output

        output = transpose_front(s_t);
    }

    /*!
     * \brief Backpropagate the errors to the previous layers
     * \param output The ETL expression into which write the output
     * \param context The training context
     */
    template <typename H, typename C, typename W, typename U>
    void backward_batch_impl(H&& output, C& context, const W& w, const U& u, size_t time_steps, size_t bptt_steps, bool direct = true) const {
        // 1. Rearrange errors

        delta_t = transpose_front(context.errors);

        // 2. Get the gradients from the context

        auto& w_grad = std::get<0>(context.up.context)->grad;
        auto& u_grad = std::get<1>(context.up.context)->grad;
        auto& b_grad = std::get<2>(context.up.context)->grad;

        w_grad = 0;
        u_grad = 0;
        b_grad = 0;

        // 3. Backpropagation through time

        size_t ttt = time_steps - 1;

        do {
            const size_t last_step = std::max(int(time_steps) - int(bptt_steps), 0);

            // Backpropagation through time
            for (int tt = ttt; tt >= int(last_step); --tt) {
                const size_t t = tt;

                if(t == time_steps - 1){
                    d_h_t(t) = delta_t(t) >> f_derivative<activation_function>(s_t(t));
                } else {
                    d_h_t(t) = (delta_t(t) + d_h_t(t + 1)) >> f_derivative<activation_function>(s_t(t));
                }

                if (t > 0) {
                    w_grad += etl::batch_outer(s_t(t - 1), d_h_t(t));
                }

                u_grad += etl::batch_outer(x_t(t), d_h_t(t));
                b_grad += etl::bias_batch_sum_2d(d_h_t(t));

                // Gradients to the input
                d_x_t(t) = d_h_t(t) * trans(u);

                // Update for next steps
                d_h_t(t) = d_h_t(t) * trans(w);
            }

            --ttt;

            // If only the last time step is used, no need to use the other errors
            if constexpr (desc::parameters::template contains<last_only>()) {
                break;
            }
        } while (ttt != 0);

        // 3. Rearrange for the output

        if (direct) {
            output = transpose_front(d_x_t);
        }
    }

    /*!
     * \brief Compute the gradients for this layer, if any
     * \param context The trainng context
     */
    template <typename C, typename W, typename U>
    void compute_gradients_impl([[maybe_unused]] C&       context,
                                [[maybe_unused]] const W& w,
                                [[maybe_unused]] const U& u,
                                [[maybe_unused]] size_t   time_steps,
                                [[maybe_unused]] size_t   bptt_steps) const {
        if constexpr (!C::layer){
            backward_batch_impl(x_t, context, w, u, time_steps, bptt_steps, false);
        }
    }

    /*!
     * \brief Backup the weights in the secondary weights matrix
     */
    void backup_weights() {
        unique_safe_get(as_derived().bak_w) = as_derived().w;
        unique_safe_get(as_derived().bak_u) = as_derived().u;
        unique_safe_get(as_derived().bak_b) = as_derived().b;
    }

    /*!
     * \brief Restore the weights from the secondary weights matrix
     */
    void restore_weights() {
        as_derived().w = *as_derived().bak_w;
        as_derived().u = *as_derived().bak_u;
        as_derived().b = *as_derived().bak_b;
    }

    /*!
     * \brief Load the weigts into the given stream
     */
    void store(std::ostream& os) const {
        cpp::binary_write_all(os, as_derived().w);
        cpp::binary_write_all(os, as_derived().u);
        cpp::binary_write_all(os, as_derived().b);
    }

    /*!
     * \brief Load the weigts from the given stream
     */
    void load(std::istream& is) {
        cpp::binary_load_all(is, as_derived().w);
        cpp::binary_load_all(is, as_derived().u);
        cpp::binary_load_all(is, as_derived().b);
    }

    /*!
     * \brief Load the weigts into the given file
     */
    void store(const std::string& file) const {
        std::ofstream os(file, std::ofstream::binary);
        store(os);
    }

    /*!
     * \brief Load the weigts from the given file
     */
    void load(const std::string& file) {
        std::ifstream is(file, std::ifstream::binary);
        load(is);
    }

    /*!
     * \brief Returns the trainable variables of this layer.
     * \return a tuple containing references to the variables of this layer
     */
    decltype(auto) trainable_parameters() {
        return std::make_tuple(std::ref(as_derived().w), std::ref(as_derived().u), std::ref(as_derived().b));
    }

    /*!
     * \brief Returns the trainable variables of this layer.
     * \return a tuple containing references to the variables of this layer
     */
    decltype(auto) trainable_parameters() const {
        return std::make_tuple(std::cref(as_derived().w), std::cref(as_derived().u), std::cref(as_derived().b));
    }

private:
    //CRTP Deduction

    /*!
     * \brief Returns a reference to the derived object, i.e. the object using the CRTP injector.
     * \return a reference to the derived object.
     */
    derived_t& as_derived() {
        return *static_cast<derived_t*>(this);
    }

    /*!
     * \brief Returns a reference to the derived object, i.e. the object using the CRTP injector.
     * \return a reference to the derived object.
     */
    const derived_t& as_derived() const {
        return *static_cast<const derived_t*>(this);
    }
};

} //end of dll namespace
<commit_msg>Improve GPU performance of RNN<commit_after>//=======================================================================
// Copyright (c) 2014-2020 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

#include <fstream>

#include "cpp_utils/assert.hpp" //Assertions
#include "cpp_utils/io.hpp"     // For binary writing

#include "etl/etl.hpp"

#include "layer.hpp"
#include "layer_traits.hpp"
#include "util/tmp.hpp"

namespace dll {

/*!
 * \brief Base class for RNN layers (fast / dynamic)
 */
template <typename Derived, typename Desc>
struct base_rnn_layer : layer<Derived> {
    using desc      = Desc;                            ///< The descriptor of the layer
    using derived_t = Derived;                         ///< The derived type (CRTP)
    using weight    = typename desc::weight;           ///< The data type for this layer
    using this_type = base_rnn_layer<derived_t, desc>; ///< The type of this layer
    using base_type = layer<Derived>;                  ///< The base type

    static constexpr auto activation_function = desc::activation_function; ///< The layer's activation function

    /*!
     * \brief Initialize the neural layer
     */
    base_rnn_layer()
            : base_type() {
        // Nothing to init here
    }

    base_rnn_layer(const base_rnn_layer& rhs) = delete;
    base_rnn_layer(base_rnn_layer&& rhs)      = delete;

    base_rnn_layer& operator=(const base_rnn_layer& rhs) = delete;
    base_rnn_layer& operator=(base_rnn_layer&& rhs) = delete;

    mutable etl::dyn_matrix<float, 3> x_t;
    mutable etl::dyn_matrix<float, 3> s_t;

    mutable etl::dyn_matrix<float, 3> delta_t;
    mutable etl::dyn_matrix<float, 3> d_h_t;
    mutable etl::dyn_matrix<float, 3> d_x_t;

    void prepare_cache(size_t Batch, size_t time_steps, size_t sequence_length, size_t hidden_units) const {
        if (cpp_unlikely(!x_t.memory_start())) {
            // Forward cache
            x_t.resize(time_steps, Batch, sequence_length);
            s_t.resize(time_steps, Batch, hidden_units);

            // Backward cache
            delta_t.resize(time_steps, Batch, hidden_units);
            d_h_t.resize(time_steps, Batch, hidden_units);
            d_x_t.resize(time_steps, Batch, sequence_length);

#ifdef ETL_GPU
            // Note: all these matrices are only accessed through sub views
            // So, the base CPU/GPU is never fully updated.
            // Starting with valid GPU status ensures that we never copy between
            // CPU and GPU
            d_h_t.ensure_gpu_up_to_date();
            s_t.ensure_gpu_up_to_date();
            d_h_t.ensure_gpu_up_to_date();
            d_x_t.ensure_gpu_up_to_date();
#endif
        }
    }

    /*!
     * \brief Apply the layer to the given batch of input.
     *
     * \param x A batch of input
     * \param output A batch of output that will be filled
     * \param w The W weights matrix
     * \param u The U weights matrix
     */
    template <typename H, typename V, typename W, typename U, typename B>
    void forward_batch_impl(H&& output, const V& x, const W& w, const U& u, const B& b, size_t time_steps, size_t sequence_length, size_t hidden_units) const {
        const auto Batch = etl::dim<0>(x);

        prepare_cache(Batch, time_steps, sequence_length, hidden_units);

        // 1. Rearrange input
        x_t = transpose_front(x);

        // 2. Forward propagation through time

        // t == 0

        s_t(0) = f_activate<activation_function>(bias_add_2d(x_t(0) * u, b));

        for (size_t t = 1; t < time_steps; ++t) {
            s_t(t) = f_activate<activation_function>(bias_add_2d(x_t(t) * u + s_t(t - 1) * w, b));
        }

        // 3. Rearrange the output

        output = transpose_front(s_t);
    }

    /*!
     * \brief Backpropagate the errors to the previous layers
     * \param output The ETL expression into which write the output
     * \param context The training context
     */
    template <typename H, typename C, typename W, typename U>
    void backward_batch_impl(H&& output, C& context, const W& w, const U& u, size_t time_steps, size_t bptt_steps, bool direct = true) const {
        // 1. Rearrange errors

        delta_t = transpose_front(context.errors);

        // 2. Get the gradients from the context

        auto& w_grad = std::get<0>(context.up.context)->grad;
        auto& u_grad = std::get<1>(context.up.context)->grad;
        auto& b_grad = std::get<2>(context.up.context)->grad;

        w_grad = 0;
        u_grad = 0;
        b_grad = 0;

        // 3. Backpropagation through time

        size_t ttt = time_steps - 1;

        do {
            const size_t last_step = std::max(int(time_steps) - int(bptt_steps), 0);

            // Backpropagation through time
            for (int tt = ttt; tt >= int(last_step); --tt) {
                const size_t t = tt;

                if(t == time_steps - 1){
                    d_h_t(t) = delta_t(t) >> f_derivative<activation_function>(s_t(t));
                } else {
                    d_h_t(t) = (delta_t(t) + d_h_t(t + 1)) >> f_derivative<activation_function>(s_t(t));
                }

                if (t > 0) {
                    w_grad += etl::batch_outer(s_t(t - 1), d_h_t(t));
                }

                u_grad += etl::batch_outer(x_t(t), d_h_t(t));
                b_grad += etl::bias_batch_sum_2d(d_h_t(t));

                // Gradients to the input
                d_x_t(t) = d_h_t(t) * trans(u);

                // Update for next steps
                d_h_t(t) = d_h_t(t) * trans(w);
            }

            --ttt;

            // If only the last time step is used, no need to use the other errors
            if constexpr (desc::parameters::template contains<last_only>()) {
                break;
            }
        } while (ttt != 0);

        // 3. Rearrange for the output

        if (direct) {
            output = transpose_front(d_x_t);
        }
    }

    /*!
     * \brief Compute the gradients for this layer, if any
     * \param context The trainng context
     */
    template <typename C, typename W, typename U>
    void compute_gradients_impl([[maybe_unused]] C&       context,
                                [[maybe_unused]] const W& w,
                                [[maybe_unused]] const U& u,
                                [[maybe_unused]] size_t   time_steps,
                                [[maybe_unused]] size_t   bptt_steps) const {
        if constexpr (!C::layer){
            backward_batch_impl(x_t, context, w, u, time_steps, bptt_steps, false);
        }
    }

    /*!
     * \brief Backup the weights in the secondary weights matrix
     */
    void backup_weights() {
        unique_safe_get(as_derived().bak_w) = as_derived().w;
        unique_safe_get(as_derived().bak_u) = as_derived().u;
        unique_safe_get(as_derived().bak_b) = as_derived().b;
    }

    /*!
     * \brief Restore the weights from the secondary weights matrix
     */
    void restore_weights() {
        as_derived().w = *as_derived().bak_w;
        as_derived().u = *as_derived().bak_u;
        as_derived().b = *as_derived().bak_b;
    }

    /*!
     * \brief Load the weigts into the given stream
     */
    void store(std::ostream& os) const {
        cpp::binary_write_all(os, as_derived().w);
        cpp::binary_write_all(os, as_derived().u);
        cpp::binary_write_all(os, as_derived().b);
    }

    /*!
     * \brief Load the weigts from the given stream
     */
    void load(std::istream& is) {
        cpp::binary_load_all(is, as_derived().w);
        cpp::binary_load_all(is, as_derived().u);
        cpp::binary_load_all(is, as_derived().b);
    }

    /*!
     * \brief Load the weigts into the given file
     */
    void store(const std::string& file) const {
        std::ofstream os(file, std::ofstream::binary);
        store(os);
    }

    /*!
     * \brief Load the weigts from the given file
     */
    void load(const std::string& file) {
        std::ifstream is(file, std::ifstream::binary);
        load(is);
    }

    /*!
     * \brief Returns the trainable variables of this layer.
     * \return a tuple containing references to the variables of this layer
     */
    decltype(auto) trainable_parameters() {
        return std::make_tuple(std::ref(as_derived().w), std::ref(as_derived().u), std::ref(as_derived().b));
    }

    /*!
     * \brief Returns the trainable variables of this layer.
     * \return a tuple containing references to the variables of this layer
     */
    decltype(auto) trainable_parameters() const {
        return std::make_tuple(std::cref(as_derived().w), std::cref(as_derived().u), std::cref(as_derived().b));
    }

private:
    //CRTP Deduction

    /*!
     * \brief Returns a reference to the derived object, i.e. the object using the CRTP injector.
     * \return a reference to the derived object.
     */
    derived_t& as_derived() {
        return *static_cast<derived_t*>(this);
    }

    /*!
     * \brief Returns a reference to the derived object, i.e. the object using the CRTP injector.
     * \return a reference to the derived object.
     */
    const derived_t& as_derived() const {
        return *static_cast<const derived_t*>(this);
    }
};

} //end of dll namespace
<|endoftext|>