<commit_before>#include "font.h"

#include <Tempest/Except>
#include <Tempest/Painter>
#include <Tempest/Platform>
#include <Tempest/Log>
#include "../utility/utf8_helper.h"
#include "thirdparty/stb_truetype.h"

#include <unordered_map>
#include <bitset>

#ifdef __WINDOWS__
#include <Shlobj.h>
#endif

using namespace Tempest;

namespace Tempest {
namespace Detail {
  template<size_t lvl>
  struct Bucket {
    std::unique_ptr<Bucket<lvl-1>> next[256];
    };

  template<>
  struct Bucket<1> {
    FontElement::Letter     letter[256];
    std::bitset<256>        ex;
    };

  static std::string getFontFolderPath() {
#ifdef __WINDOWS__
    char   path[MAX_PATH]={};
    SHGetSpecialFolderPathA(nullptr,path,CSIDL_FONTS,false);
    return path;
#else
    return "";
#endif
    }

  static std::string getFallbackFont() {
#ifdef __WINDOWS__
    return getFontFolderPath()+"\\georgia.ttf";
#else
    return "";
#endif
    }
  }

}

struct FontElement::LetterTable {
  struct Chunk {
    uint32_t          size=0;
    Detail::Bucket<3> next[256];
    };

  std::vector<Chunk> chunk;

  Letter& at(float sz,char32_t ch){
    return *implFind(sz,ch,true);
    }

  Letter* find(float sz,char32_t ch){
    return implFind(sz,ch,false);
    }

  Letter* implFind(float sz,char32_t ch,bool create){
    uint32_t size = uint32_t(sz*100);

    for(auto& i:chunk)
      if(i.size==size)
        return implFind(i,uint32_t(ch),create);

    chunk.emplace_back();
    chunk.back().size=size;
    return implFind(chunk.back(),uint32_t(ch),create);
    }

  Letter* implFind(Chunk& c,uint32_t ch,bool create){
    uint8_t* key = reinterpret_cast<uint8_t*>(&ch);
    auto&    b   = c.next[*key];
    return implFind(b,key+1,create);
    }

  template<size_t lvl>
  Letter* implFind(Detail::Bucket<lvl>& b,const uint8_t* k,bool create){
    auto& ptr = b.next[*k];
    if(ptr==nullptr && create)
      ptr.reset(new Detail::Bucket<lvl-1>());
    if(ptr==nullptr)
      return nullptr;
    return implFind(*b.next[*k],k+1,create);
    }

  Letter* implFind(Detail::Bucket<1>& b,const uint8_t* pk,bool create){
    auto k = *pk;
    if(!b.ex[k]){
      if(create){
        b.ex[k]=true;
        return &b.letter[k];
        }
      return nullptr;
      }
    return &b.letter[k];
    }
  };

struct FontElement::Impl {
  enum { MIN_BUF_SZ=512 };

  template<class CharT>
  Impl(const CharT *filename) {
    if(filename==nullptr)
      return;

    RFile file(filename);
    size = file.size();
    data = new uint8_t[size];

    if(file.read(data,size)!=size) {
      delete data;
      throw std::system_error(Tempest::SystemErrc::UnableToLoadAsset);
      }
    }

  ~Impl() {
    delete data;
    std::free(rasterBuf);
    }

  uint8_t* ttfMalloc(size_t sz){
    if(sz<rasterSz)
      return rasterBuf;
    sz = std::max<size_t>(sz,MIN_BUF_SZ);
    void* p=std::realloc(rasterBuf,sz);
    if(p==nullptr)
      return nullptr;
    rasterBuf=reinterpret_cast<uint8_t*>(p);
    rasterSz =sz;
    return rasterBuf;
    }

  uint8_t* getGlyphBitmapSubpixel(stbtt_fontinfo *info,
                                  float scale,int  glyph,
                                  int&  width,int& height,
                                  int&  xoff, int& yoff) {
    assert(scale>0.f);

    stbtt_vertex *vertices;
    int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);

    int ix0,iy0,ix1,iy1;
    stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.f/*shift_x*/, 0.f/*shift_y*/, &ix0,&iy0,&ix1,&iy1);

    stbtt__bitmap gbm={};
    // now we get the size
    gbm.w = (ix1 - ix0);
    gbm.h = (iy1 - iy0);

    width  = gbm.w;
    height = gbm.h;

    xoff   = ix0;
    yoff   = iy0;

    if(gbm.w>0 && gbm.h>0) {
      gbm.pixels = ttfMalloc(size_t(gbm.w*gbm.h));
      if(gbm.pixels!=nullptr) {
        gbm.stride = gbm.w;
        stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale, scale, 0.f/*shift_x*/, 0.f/*shift_y*/, ix0, iy0, 1, info->userdata);
        }
      }

    STBTT_free(vertices,info->userdata);
    return gbm.pixels;
    }

  static const Letter& nullLater(){
    static const Letter l;
    return l;
    }

  const Letter& letter(char32_t ch,float size,TextureAtlas* tex) {
    {
    std::lock_guard<std::mutex> guard(syncMap);
    auto cc=map.find(size,ch);
    if(cc!=nullptr){
      if(cc->hasView || tex==nullptr)
        return *cc;
      }
    }

    if(this->size==0)
      return nullLater();
    return allocLetter(ch,size,tex,false);
    }

  const Letter& allocLetter(char32_t ch,float size,TextureAtlas* tex,bool fallback) {
    const float scale = stbtt_ScaleForPixelHeight(&info,size); //size/(ascent-descent);
    if(!(scale>0.f))
      return nullLater();

    int w=0,h=0,dx=0,dy=0;
    int ax=0;

    const int index = stbtt_FindGlyphIndex(&info,int(ch));
    stbtt_GetGlyphHMetrics(&info,index,&ax,nullptr);

    Sprite spr;
    if(tex!=nullptr){
      std::lock_guard<std::mutex> guard(syncMem);
      uint8_t* bitmap=getGlyphBitmapSubpixel(&info,scale,index,w,h,dx,dy);
      if(bitmap!=nullptr)
        spr = tex->load(bitmap,uint32_t(w),uint32_t(h),Pixmap::Format::A);
      } else {
      int ix0=0,ix1=0,iy0=0,iy1=0;
      stbtt_GetGlyphBitmapBoxSubpixel(&info,index,scale,scale,0.f,0.f,&ix0,&iy0,&ix1,&iy1);

      w  = (ix1 - ix0);
      h  = (iy1 - iy0);
      dx = ix0;
      dy = iy0;
      }

    if((w<=0 || h<=0) && ax==0) {
      if(!fallback)
        return allocFallbackLetter(ch,size,tex);
      return nullLater();
      }

    std::lock_guard<std::mutex> guard(syncMap);
    Letter& lt = map.at(size,ch);
    lt.view    = std::move(spr);
    lt.size    = Size(w,h);
    lt.dpos    = Point(dx,dy);
    lt.advance = Point(int(ax*scale),int(lineGap*scale));
    lt.hasView = (tex!=nullptr);
    return lt;
    }

  const Letter& allocFallbackLetter(char32_t ch,float size,TextureAtlas* tex) {
    try {
      std::lock_guard<std::mutex> guard(syncMap);
      if(fallback==nullptr){
        fallback.reset(new Impl(Detail::getFallbackFont().c_str()));
        if(stbtt_InitFont(&fallback->info,fallback->data,0)==0)
          throw std::system_error(Tempest::SystemErrc::UnableToLoadAsset);
        }

      Letter  lf = fallback->allocLetter(ch,size,tex,true);
      Letter& lt = map.at(size,ch);
      lt = lf;
      return lt;
      }
    catch (...) {
      return nullLater();
      }
    }

  uint8_t*       data=nullptr;
  uint32_t       size=0;
  stbtt_fontinfo info={};

  std::mutex     syncMem;
  uint8_t*       rasterBuf=nullptr;
  size_t         rasterSz =0;

  int ascent =0;
  int descent=0;
  int lineGap=0;

  std::mutex                           syncMap;
  LetterTable                          map;
  std::unique_ptr<Impl>                fallback;
  };

FontElement::FontElement()
  :FontElement(static_cast<const char*>(nullptr)){
  }

FontElement::FontElement(std::nullptr_t)
  :FontElement(){
  }

template<class CharT>
FontElement::FontElement(const CharT *file,std::true_type)
  :ptr(std::make_shared<Impl>(file)) {
  if(file) {
    if(stbtt_InitFont(&ptr->info,ptr->data,0)==0)
      throw std::system_error(Tempest::SystemErrc::UnableToLoadAsset);
    stbtt_GetFontVMetrics(&ptr->info,&ptr->ascent,&ptr->descent,&ptr->lineGap);
    }
  }

FontElement::FontElement(const char *file)
  :FontElement(file,std::true_type()) {
  }

FontElement::FontElement(const char16_t *file)
  :FontElement(file,std::true_type()) {
  }

FontElement::FontElement(const std::string& file)
  :FontElement(file.c_str(),std::true_type()) {
  }

FontElement::FontElement(const std::u16string& file)
  :FontElement(file.c_str(),std::true_type()) {
  }

const FontElement::LetterGeometry& FontElement::letterGeometry(char32_t ch, float size) const { //FIXME: UB?
  return reinterpret_cast<const LetterGeometry&>(ptr->letter(ch,size,nullptr));
  }

const FontElement::Letter& FontElement::letter(char32_t ch, float size, TextureAtlas &tex) const {
  return ptr->letter(ch,size,&tex);
  }

Size FontElement::textSize(const char *text,float fontSize) const {
  Utf8Iterator i(text,std::strlen(text));

  Size ret;
  while(i.hasData()){
    char32_t c = i.next();
    if(c=='\0')
      return ret;
    auto& g = letterGeometry(c,fontSize);

    ret.h = std::max(ret.h,g.size.h);
    ret.w += g.advance.x;
    }

  return ret;
  }

template<class CharT>
Font::Font(const CharT *file,std::true_type)
  : fnt{{file,nullptr},{nullptr,nullptr}}{
  fnt[1][0]=fnt[0][0];
  fnt[1][1]=fnt[0][0];
  fnt[0][1]=fnt[0][0];
  }

Font::Font(const char *file)
  : Font(file,std::true_type()){
  }

Font::Font(const std::string &file)
  : Font(file.c_str(),std::true_type()){
  }

Font::Font(const char16_t *file)
  : Font(file,std::true_type()){
  }

Font::Font(const std::u16string &file)
  : Font(file.c_str(),std::true_type()){
  }

void Font::setPixelSize(float sz) {
  size=sz;
  }

const Font::LetterGeometry &Font::letterGeometry(char16_t ch) const {
  return fnt[0][0].letterGeometry(ch,size);
  }

const Font::LetterGeometry &Font::letterGeometry(char32_t ch) const {
  return fnt[0][0].letterGeometry(ch,size); //TODO
  }

const Font::Letter &Font::letter(char16_t ch, TextureAtlas &tex) const {
  return fnt[0][0].letter(ch,size,tex);
  }

const Font::Letter &Font::letter(char32_t ch, TextureAtlas &tex) const {
  return fnt[0][0].letter(ch,size,tex); //TODO
  }

const Font::Letter &Font::letter(char16_t ch, Painter &p) const {
  return letter(ch,p.ta);
  }

const Font::Letter &Font::letter(char32_t ch, Painter &p) const {
  return letter(ch,p.ta);
  }

Size Font::textSize(const char *text) const {
  return fnt[0][0].textSize(text,size);
  }

Size Font::textSize(const std::string &text) const {
  return textSize(text.c_str());
  }
<commit_msg>perfomance: fix empty font allocation<commit_after>#include "font.h"

#include <Tempest/Except>
#include <Tempest/Painter>
#include <Tempest/Platform>
#include <Tempest/Log>
#include "../utility/utf8_helper.h"
#include "thirdparty/stb_truetype.h"

#include <unordered_map>
#include <bitset>

#ifdef __WINDOWS__
#include <Shlobj.h>
#endif

using namespace Tempest;

namespace Tempest {
namespace Detail {
  template<size_t lvl>
  struct Bucket {
    std::unique_ptr<Bucket<lvl-1>> next[256];
    };

  template<>
  struct Bucket<1> {
    FontElement::Letter     letter[256];
    std::bitset<256>        ex;
    };

  static std::string getFontFolderPath() {
#ifdef __WINDOWS__
    char   path[MAX_PATH]={};
    SHGetSpecialFolderPathA(nullptr,path,CSIDL_FONTS,false);
    return path;
#else
    return "";
#endif
    }

  static std::string getFallbackFont() {
#ifdef __WINDOWS__
    return getFontFolderPath()+"\\georgia.ttf";
#else
    return "";
#endif
    }
  }

}

struct FontElement::LetterTable {
  struct Chunk {
    uint32_t          size=0;
    Detail::Bucket<3> next[256];
    };

  std::vector<Chunk> chunk;

  Letter& at(float sz,char32_t ch){
    return *implFind(sz,ch,true);
    }

  Letter* find(float sz,char32_t ch){
    return implFind(sz,ch,false);
    }

  Letter* implFind(float sz,char32_t ch,bool create){
    uint32_t size = uint32_t(sz*100);

    for(auto& i:chunk)
      if(i.size==size)
        return implFind(i,uint32_t(ch),create);

    chunk.emplace_back();
    chunk.back().size=size;
    return implFind(chunk.back(),uint32_t(ch),create);
    }

  Letter* implFind(Chunk& c,uint32_t ch,bool create){
    uint8_t* key = reinterpret_cast<uint8_t*>(&ch);
    auto&    b   = c.next[*key];
    return implFind(b,key+1,create);
    }

  template<size_t lvl>
  Letter* implFind(Detail::Bucket<lvl>& b,const uint8_t* k,bool create){
    auto& ptr = b.next[*k];
    if(ptr==nullptr && create)
      ptr.reset(new Detail::Bucket<lvl-1>());
    if(ptr==nullptr)
      return nullptr;
    return implFind(*b.next[*k],k+1,create);
    }

  Letter* implFind(Detail::Bucket<1>& b,const uint8_t* pk,bool create){
    auto k = *pk;
    if(!b.ex[k]){
      if(create){
        b.ex[k]=true;
        return &b.letter[k];
        }
      return nullptr;
      }
    return &b.letter[k];
    }
  };

struct FontElement::Impl {
  enum { MIN_BUF_SZ=512 };

  template<class CharT>
  Impl(const CharT *filename) {
    if(filename==nullptr)
      return;

    RFile file(filename);
    size = file.size();
    data = new uint8_t[size];

    if(file.read(data,size)!=size) {
      delete data;
      throw std::system_error(Tempest::SystemErrc::UnableToLoadAsset);
      }
    }

  ~Impl() {
    delete data;
    std::free(rasterBuf);
    }

  uint8_t* ttfMalloc(size_t sz){
    if(sz<rasterSz)
      return rasterBuf;
    sz = std::max<size_t>(sz,MIN_BUF_SZ);
    void* p=std::realloc(rasterBuf,sz);
    if(p==nullptr)
      return nullptr;
    rasterBuf=reinterpret_cast<uint8_t*>(p);
    rasterSz =sz;
    return rasterBuf;
    }

  uint8_t* getGlyphBitmapSubpixel(stbtt_fontinfo *info,
                                  float scale,int  glyph,
                                  int&  width,int& height,
                                  int&  xoff, int& yoff) {
    assert(scale>0.f);

    stbtt_vertex *vertices;
    int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);

    int ix0,iy0,ix1,iy1;
    stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.f/*shift_x*/, 0.f/*shift_y*/, &ix0,&iy0,&ix1,&iy1);

    stbtt__bitmap gbm={};
    // now we get the size
    gbm.w = (ix1 - ix0);
    gbm.h = (iy1 - iy0);

    width  = gbm.w;
    height = gbm.h;

    xoff   = ix0;
    yoff   = iy0;

    if(gbm.w>0 && gbm.h>0) {
      gbm.pixels = ttfMalloc(size_t(gbm.w*gbm.h));
      if(gbm.pixels!=nullptr) {
        gbm.stride = gbm.w;
        stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale, scale, 0.f/*shift_x*/, 0.f/*shift_y*/, ix0, iy0, 1, info->userdata);
        }
      }

    STBTT_free(vertices,info->userdata);
    return gbm.pixels;
    }

  static const Letter& nullLater(){
    static const Letter l;
    return l;
    }

  const Letter& letter(char32_t ch,float size,TextureAtlas* tex) {
    {
    std::lock_guard<std::mutex> guard(syncMap);
    auto cc=map.find(size,ch);
    if(cc!=nullptr){
      if(cc->hasView || tex==nullptr)
        return *cc;
      }
    }

    if(this->size==0)
      return nullLater();
    return allocLetter(ch,size,tex,false);
    }

  const Letter& allocLetter(char32_t ch,float size,TextureAtlas* tex,bool fallback) {
    const float scale = stbtt_ScaleForPixelHeight(&info,size); //size/(ascent-descent);
    if(!(scale>0.f))
      return nullLater();

    int w=0,h=0,dx=0,dy=0;
    int ax=0;

    const int index = stbtt_FindGlyphIndex(&info,int(ch));
    stbtt_GetGlyphHMetrics(&info,index,&ax,nullptr);

    Sprite spr;
    if(tex!=nullptr){
      std::lock_guard<std::mutex> guard(syncMem);
      uint8_t* bitmap=getGlyphBitmapSubpixel(&info,scale,index,w,h,dx,dy);
      if(bitmap!=nullptr)
        spr = tex->load(bitmap,uint32_t(w),uint32_t(h),Pixmap::Format::A);
      } else {
      int ix0=0,ix1=0,iy0=0,iy1=0;
      stbtt_GetGlyphBitmapBoxSubpixel(&info,index,scale,scale,0.f,0.f,&ix0,&iy0,&ix1,&iy1);

      w  = (ix1 - ix0);
      h  = (iy1 - iy0);
      dx = ix0;
      dy = iy0;
      }

    if((w<=0 || h<=0) && ax==0) {
      if(!fallback)
        return allocFallbackLetter(ch,size,tex);
      return nullLater();
      }

    std::lock_guard<std::mutex> guard(syncMap);
    Letter& lt = map.at(size,ch);
    lt.view    = std::move(spr);
    lt.size    = Size(w,h);
    lt.dpos    = Point(dx,dy);
    lt.advance = Point(int(ax*scale),int(lineGap*scale));
    lt.hasView = (tex!=nullptr);
    return lt;
    }

  const Letter& allocFallbackLetter(char32_t ch,float size,TextureAtlas* tex) {
    try {
      std::lock_guard<std::mutex> guard(syncMap);
      if(fallback==nullptr){
        fallback.reset(new Impl(Detail::getFallbackFont().c_str()));
        if(stbtt_InitFont(&fallback->info,fallback->data,0)==0)
          throw std::system_error(Tempest::SystemErrc::UnableToLoadAsset);
        }

      Letter  lf = fallback->allocLetter(ch,size,tex,true);
      Letter& lt = map.at(size,ch);
      lt = lf;
      return lt;
      }
    catch (...) {
      return nullLater();
      }
    }

  uint8_t*       data=nullptr;
  uint32_t       size=0;
  stbtt_fontinfo info={};

  std::mutex     syncMem;
  uint8_t*       rasterBuf=nullptr;
  size_t         rasterSz =0;

  int ascent =0;
  int descent=0;
  int lineGap=0;

  std::mutex                           syncMap;
  LetterTable                          map;
  std::unique_ptr<Impl>                fallback;
  };

FontElement::FontElement() {
  static std::shared_ptr<Impl> dummy = std::make_shared<Impl>(static_cast<const char*>(nullptr));
  ptr = dummy;
  }

FontElement::FontElement(std::nullptr_t)
  :FontElement(){
  }

template<class CharT>
FontElement::FontElement(const CharT *file,std::true_type)
  :ptr(std::make_shared<Impl>(file)) {
  if(file) {
    if(stbtt_InitFont(&ptr->info,ptr->data,0)==0)
      throw std::system_error(Tempest::SystemErrc::UnableToLoadAsset);
    stbtt_GetFontVMetrics(&ptr->info,&ptr->ascent,&ptr->descent,&ptr->lineGap);
    }
  }

FontElement::FontElement(const char *file)
  :FontElement(file,std::true_type()) {
  }

FontElement::FontElement(const char16_t *file)
  :FontElement(file,std::true_type()) {
  }

FontElement::FontElement(const std::string& file)
  :FontElement(file.c_str(),std::true_type()) {
  }

FontElement::FontElement(const std::u16string& file)
  :FontElement(file.c_str(),std::true_type()) {
  }

const FontElement::LetterGeometry& FontElement::letterGeometry(char32_t ch, float size) const { //FIXME: UB?
  return reinterpret_cast<const LetterGeometry&>(ptr->letter(ch,size,nullptr));
  }

const FontElement::Letter& FontElement::letter(char32_t ch, float size, TextureAtlas &tex) const {
  return ptr->letter(ch,size,&tex);
  }

Size FontElement::textSize(const char *text,float fontSize) const {
  Utf8Iterator i(text,std::strlen(text));

  Size ret;
  while(i.hasData()){
    char32_t c = i.next();
    if(c=='\0')
      return ret;
    auto& g = letterGeometry(c,fontSize);

    ret.h = std::max(ret.h,g.size.h);
    ret.w += g.advance.x;
    }

  return ret;
  }

template<class CharT>
Font::Font(const CharT *file,std::true_type)
  : fnt{{file,nullptr},{nullptr,nullptr}}{
  fnt[1][0]=fnt[0][0];
  fnt[1][1]=fnt[0][0];
  fnt[0][1]=fnt[0][0];
  }

Font::Font(const char *file)
  : Font(file,std::true_type()){
  }

Font::Font(const std::string &file)
  : Font(file.c_str(),std::true_type()){
  }

Font::Font(const char16_t *file)
  : Font(file,std::true_type()){
  }

Font::Font(const std::u16string &file)
  : Font(file.c_str(),std::true_type()){
  }

void Font::setPixelSize(float sz) {
  size=sz;
  }

const Font::LetterGeometry &Font::letterGeometry(char16_t ch) const {
  return fnt[0][0].letterGeometry(ch,size);
  }

const Font::LetterGeometry &Font::letterGeometry(char32_t ch) const {
  return fnt[0][0].letterGeometry(ch,size); //TODO
  }

const Font::Letter &Font::letter(char16_t ch, TextureAtlas &tex) const {
  return fnt[0][0].letter(ch,size,tex);
  }

const Font::Letter &Font::letter(char32_t ch, TextureAtlas &tex) const {
  return fnt[0][0].letter(ch,size,tex); //TODO
  }

const Font::Letter &Font::letter(char16_t ch, Painter &p) const {
  return letter(ch,p.ta);
  }

const Font::Letter &Font::letter(char32_t ch, Painter &p) const {
  return letter(ch,p.ta);
  }

Size Font::textSize(const char *text) const {
  return fnt[0][0].textSize(text,size);
  }

Size Font::textSize(const std::string &text) const {
  return textSize(text.c_str());
  }
<|endoftext|>


<commit_before>// UjoImro, 2013
// Experimental Research Code for the CARP Project

#ifndef __CARP__UTILITY__HPP__
#define __CARP__UTILITY__HPP__

#include <chrono>
#include <string>
#include <iomanip>
#include <iostream>

#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/ocl/ocl.hpp>

#include <boost/filesystem.hpp>

//#define PRINT(var)  std::cout << "debug: " << BOOST_PP_STRINGIZE(var) << " = " << var << std::endl
#define PRINT(var)

namespace carp {

class Timing
{
    std::vector<double> cpu_timings;
    std::vector<double> gpu_p_copy_timings;
    std::vector<double> gpu_nocopy_timings;
    std::vector<double> pen_timings;
public:
    Timing(const std::string & name) {
        std::cout << "Measuring performance of " << name << std::endl;
        std::cout << " CPU Time - GPU+copy - GPU Time - PEN Time" << std::endl;
    }

    void print( const std::chrono::duration<double> &cpu, const std::chrono::duration<double> &gpu_p_copy, const std::chrono::duration<double> &gpu_nocopy, const std::chrono::duration<double> &pen ) {
        std::cout << std::fixed << std::setprecision(3);
        std::cout << std::setw(8) << cpu       .count() << "s -";
        std::cout << std::setw(8) << gpu_p_copy.count() << "s -";
        std::cout << std::setw(8) << gpu_nocopy.count() << "s -";
        std::cout << std::setw(8) << pen       .count() << "s" << std::endl;

	cpu_timings       .push_back(cpu       .count());
        gpu_p_copy_timings.push_back(gpu_p_copy.count());
        gpu_nocopy_timings.push_back(gpu_nocopy.count());
        pen_timings       .push_back(pen       .count());
    }

    ~Timing() {
        std::cout << "    Total CPU time           : " << std::accumulate(cpu_timings       .begin(),cpu_timings       .end(),0.0) << "s\n";
        std::cout << "    Total GPU time (inc copy): " << std::accumulate(gpu_p_copy_timings.begin(),gpu_p_copy_timings.end(),0.0) << "s\n";
        std::cout << "    Total GPU time (w/o copy): " << std::accumulate(gpu_nocopy_timings.begin(),gpu_nocopy_timings.end(),0.0) << "s\n";
        std::cout << "    Total Pen time           : " << std::accumulate(pen_timings       .begin(),pen_timings       .end(),0.0) << "s\n";
    }
};

class record_t {
private:
    boost::filesystem::path m_path;

public:
    cv::Mat cpuimg() const {
        return cv::imread(m_path.string());
    }

    std::string path() const {
        return m_path.string();
    }

    record_t( const boost::filesystem::path & path )
    : m_path(path)  { }

}; // record_t

template <class T0>
std::vector<record_t>
get_pool( T0 pathname )
{
    boost::filesystem::path path(pathname);

    if ( (!boost::filesystem::exists(path)) or (!boost::filesystem::is_directory(path)) )
        throw std::runtime_error( std::string("Directory `") + pathname + "' does not exists. The directory should contain the testing images.");

    std::vector<record_t> pool;

    boost::filesystem::directory_iterator end_iter;
    for ( boost::filesystem::directory_iterator iter(path); iter!= end_iter; ++iter )
    {
        if (boost::filesystem::is_regular_file(iter->status()))
        {
            std::string extension = iter->path().extension().string();
            if ( (extension ==".jpg") or (extension==".jpeg") ) {
                PRINT(iter->path().string());
                pool.push_back(record_t(iter->path()));
            }
        }
    }

    return pool;
} // get_pool

static std::map<int, std::string> borders
{{ cv::BORDER_CONSTANT   , "BORDER_CONSTANT"    }
,{ cv::BORDER_REPLICATE  , "BORDER_REPLICATE"   }
,{ cv::BORDER_REFLECT    , "BORDER_REFLECT"     }
,{ cv::BORDER_WRAP       , "BORDER_WRAP"        }
,{ cv::BORDER_REFLECT_101, "BORDER_REFLECT_101" }
};

} // namespace carp

#endif /* __CARP__UTILITY__HPP__ */
<commit_msg>Do not print s after printing the time in include/utility.hpp so that the outputs can be used easily in scripts<commit_after>// UjoImro, 2013
// Experimental Research Code for the CARP Project

#ifndef __CARP__UTILITY__HPP__
#define __CARP__UTILITY__HPP__

#include <chrono>
#include <string>
#include <iomanip>
#include <iostream>

#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/ocl/ocl.hpp>

#include <boost/filesystem.hpp>

//#define PRINT(var)  std::cout << "debug: " << BOOST_PP_STRINGIZE(var) << " = " << var << std::endl
#define PRINT(var)

namespace carp {

class Timing
{
    std::vector<double> cpu_timings;
    std::vector<double> gpu_p_copy_timings;
    std::vector<double> gpu_nocopy_timings;
    std::vector<double> pen_timings;
public:
    Timing(const std::string & name) {
        std::cout << "Measuring performance of " << name << std::endl;
        std::cout << " CPU Time - GPU+copy - GPU Time - PEN Time" << std::endl;
    }

    void print( const std::chrono::duration<double> &cpu, const std::chrono::duration<double> &gpu_p_copy, const std::chrono::duration<double> &gpu_nocopy, const std::chrono::duration<double> &pen ) {
        std::cout << std::fixed << std::setprecision(3);
        std::cout << std::setw(8) << cpu       .count() << "s -";
        std::cout << std::setw(8) << gpu_p_copy.count() << "s -";
        std::cout << std::setw(8) << gpu_nocopy.count() << "s -";
        std::cout << std::setw(8) << pen       .count() << "s" << std::endl;

	cpu_timings       .push_back(cpu       .count());
        gpu_p_copy_timings.push_back(gpu_p_copy.count());
        gpu_nocopy_timings.push_back(gpu_nocopy.count());
        pen_timings       .push_back(pen       .count());
    }

    ~Timing() {
        std::cout << "    Total CPU time           : " << std::accumulate(cpu_timings       .begin(),cpu_timings       .end(),0.0) << "\n";
        std::cout << "    Total GPU time (inc copy): " << std::accumulate(gpu_p_copy_timings.begin(),gpu_p_copy_timings.end(),0.0) << "\n";
        std::cout << "    Total GPU time (w/o copy): " << std::accumulate(gpu_nocopy_timings.begin(),gpu_nocopy_timings.end(),0.0) << "\n";
        std::cout << "    Total Pen time           : " << std::accumulate(pen_timings       .begin(),pen_timings       .end(),0.0) << "\n";
    }
};

class record_t {
private:
    boost::filesystem::path m_path;

public:
    cv::Mat cpuimg() const {
        return cv::imread(m_path.string());
    }

    std::string path() const {
        return m_path.string();
    }

    record_t( const boost::filesystem::path & path )
    : m_path(path)  { }

}; // record_t

template <class T0>
std::vector<record_t>
get_pool( T0 pathname )
{
    boost::filesystem::path path(pathname);

    if ( (!boost::filesystem::exists(path)) or (!boost::filesystem::is_directory(path)) )
        throw std::runtime_error( std::string("Directory `") + pathname + "' does not exists. The directory should contain the testing images.");

    std::vector<record_t> pool;

    boost::filesystem::directory_iterator end_iter;
    for ( boost::filesystem::directory_iterator iter(path); iter!= end_iter; ++iter )
    {
        if (boost::filesystem::is_regular_file(iter->status()))
        {
            std::string extension = iter->path().extension().string();
            if ( (extension ==".jpg") or (extension==".jpeg") ) {
                PRINT(iter->path().string());
                pool.push_back(record_t(iter->path()));
            }
        }
    }

    return pool;
} // get_pool

static std::map<int, std::string> borders
{{ cv::BORDER_CONSTANT   , "BORDER_CONSTANT"    }
,{ cv::BORDER_REPLICATE  , "BORDER_REPLICATE"   }
,{ cv::BORDER_REFLECT    , "BORDER_REFLECT"     }
,{ cv::BORDER_WRAP       , "BORDER_WRAP"        }
,{ cv::BORDER_REFLECT_101, "BORDER_REFLECT_101" }
};

} // namespace carp

#endif /* __CARP__UTILITY__HPP__ */
<|endoftext|>


<commit_before>namespace VERSION {
    const int MAJOR = 0;
    const int MINOR = 1;
    const int PATCH = 19;
}
<commit_msg>Bump up version to v0.1.20<commit_after>namespace VERSION {
    const int MAJOR = 0;
    const int MINOR = 1;
    const int PATCH = 20;
}
<|endoftext|>


<commit_before>#include "runtime_internal.h"
#include "HalideRuntime.h"
#include "printer.h"
#include "scoped_mutex_lock.h"

// Note: The profiler thread may out-live any valid user_context, or
// be used across many different user_contexts, so nothing it calls
// can depend on the user context.

extern "C" {
// Returns the address of the global halide_profiler state
WEAK halide_profiler_state *halide_profiler_get_state() {
    static halide_profiler_state s = {{{0}}, NULL, 1, 0, 0, false};
    return &s;
}
}

namespace Halide { namespace Runtime { namespace Internal {

WEAK halide_profiler_pipeline_stats *find_or_create_pipeline(const char *pipeline_name, int num_funcs, const uint64_t *func_names) {
    halide_profiler_state *s = halide_profiler_get_state();

    for (halide_profiler_pipeline_stats *p = s->pipelines; p;
         p = (halide_profiler_pipeline_stats *)(p->next)) {
        // The same pipeline will deliver the same global constant
        // string, so they can be compared by pointer.
        if (p->name == pipeline_name &&
            p->num_funcs == num_funcs) {
            return p;
        }
    }
    // Create a new pipeline stats entry.
    halide_profiler_pipeline_stats *p =
        (halide_profiler_pipeline_stats *)malloc(sizeof(halide_profiler_pipeline_stats));
    if (!p) return NULL;
    p->next = s->pipelines;
    p->name = pipeline_name;
    p->first_func_id = s->first_free_id;
    p->num_funcs = num_funcs;
    p->runs = 0;
    p->time = 0;
    p->samples = 0;
    p->memory_current = 0;
    p->memory_peak = 0;
    p->memory_total = 0;
    p->num_allocs = 0;
    p->funcs = (halide_profiler_func_stats *)malloc(num_funcs * sizeof(halide_profiler_func_stats));
    if (!p->funcs) {
        free(p);
        return NULL;
    }
    for (int i = 0; i < num_funcs; i++) {
        p->funcs[i].time = 0;
        p->funcs[i].name = (const char *)(func_names[i]);
        p->funcs[i].memory_current = 0;
        p->funcs[i].memory_peak = 0;
        p->funcs[i].memory_total = 0;
        p->funcs[i].num_allocs = 0;
    }
    s->first_free_id += num_funcs;
    s->pipelines = p;
    return p;
}

WEAK void bill_func(halide_profiler_state *s, int func_id, uint64_t time) {
    halide_profiler_pipeline_stats *p_prev = NULL;
    for (halide_profiler_pipeline_stats *p = s->pipelines; p;
         p = (halide_profiler_pipeline_stats *)(p->next)) {
        if (func_id >= p->first_func_id && func_id < p->first_func_id + p->num_funcs) {
            if (p_prev) {
                // Bubble the pipeline to the top to speed up future queries.
                p_prev->next = (halide_profiler_pipeline_stats *)(p->next);
                p->next = s->pipelines;
                s->pipelines = p;
            }
            p->funcs[func_id - p->first_func_id].time += time;
            p->time += time;
            p->samples++;
            return;
        }
        p_prev = p;
    }
    // Someone must have called reset_state while a kernel was running. Do nothing.
}

WEAK void sampling_profiler_thread(void *) {
    halide_profiler_state *s = halide_profiler_get_state();

    // grab the lock
    halide_mutex_lock(&s->lock);

    while (s->current_func != halide_profiler_please_stop) {

        uint64_t t1 = halide_current_time_ns(NULL);
        uint64_t t = t1;
        while (1) {
            uint64_t t_now = halide_current_time_ns(NULL);
            int func = s->current_func;
            if (func == halide_profiler_please_stop) {
                break;
            } else if (func >= 0) {
                // Assume all time since I was last awake is due to
                // the currently running func.
                bill_func(s, func, t_now - t);
            }
            t = t_now;

            // Release the lock, sleep, reacquire.
            int sleep_ms = s->sleep_time;
            halide_mutex_unlock(&s->lock);
            halide_sleep_ms(NULL, sleep_ms);
            halide_mutex_lock(&s->lock);
        }
    }

    s->started = false;

    halide_mutex_unlock(&s->lock);
}

}}}

extern "C" {

// Returns a token identifying this pipeline instance.
WEAK int halide_profiler_pipeline_start(void *user_context,
                                        const char *pipeline_name,
                                        int num_funcs,
                                        const uint64_t *func_names) {
    halide_profiler_state *s = halide_profiler_get_state();

    ScopedMutexLock lock(&s->lock);

    if (!s->started) {
        halide_start_clock(user_context);
        halide_spawn_thread(user_context, sampling_profiler_thread, NULL);
        s->started = true;
    }

    halide_profiler_pipeline_stats *p =
        find_or_create_pipeline(pipeline_name, num_funcs, func_names);
    if (!p) {
        // Allocating space to track the statistics failed.
        return halide_error_out_of_memory(user_context);
    }
    p->runs++;

    return p->first_func_id;
}

WEAK void halide_profiler_memory_allocate(void *user_context,
                                          const char *pipeline_name,
                                          int token,
                                          int func_id,
                                          int incr) {
    halide_profiler_state *s = halide_profiler_get_state();
    ScopedMutexLock lock(&s->lock);

    func_id += token;

    halide_profiler_pipeline_stats *p_stats = NULL;
    for (halide_profiler_pipeline_stats *p = s->pipelines; p;
         p = (halide_profiler_pipeline_stats *)(p->next)) {
        // The same pipeline will deliver the same global constant
        // string, so they can be compared by pointer.
        if (p->name == pipeline_name) {
            p_stats = p;
            break;
        }
    }

    halide_assert(user_context, p_stats != NULL);
    halide_assert(user_context, (func_id - p_stats->first_func_id) >= 0);
    halide_assert(user_context, (func_id - p_stats->first_func_id) < p_stats->num_funcs);

    halide_profiler_func_stats *f_stats = &p_stats->funcs[func_id - p_stats->first_func_id];

    // Update per-pipeline memory stats
    p_stats->num_allocs += 1;
    p_stats->memory_total += incr;
    p_stats->memory_current += incr;
    if (p_stats->memory_current > p_stats->memory_peak) {
        p_stats->memory_peak = p_stats->memory_current;
    }

    // Update per-func memory stats
    f_stats->num_allocs += 1;
    f_stats->memory_total += incr;
    f_stats->memory_current += incr;
    if (f_stats->memory_current > f_stats->memory_peak) {
        f_stats->memory_peak = f_stats->memory_current;
    }

    /*// Update per-pipeline memory stats
    __sync_add_and_fetch(&p_stats->num_allocs, 1);
    __sync_add_and_fetch(&p_stats->memory_total, incr);
    int p_mem_current = __sync_add_and_fetch(&p_stats->memory_current, incr);
    if (p_mem_current > p_stats->memory_peak) {
        p_stats->memory_peak = p_mem_current;
    }

    // Update per-func memory stats
    _sync_add_and_fetch(&f_stats->num_allocs, incr);
    __sync_add_and_fetch(&f_stats->memory_total, incr);
    int f_mem_current = __sync_add_and_fetch(&f_stats->memory_current, incr);
    if (f_mem_current > f_stats->memory_peak) {
        f_stats->memory_peak = f_mem_current;
    }*/
}

WEAK void halide_profiler_memory_free(void *user_context,
                                      const char *pipeline_name,
                                      int token,
                                      int func_id,
                                      int decr) {
    halide_profiler_state *s = halide_profiler_get_state();
    ScopedMutexLock lock(&s->lock);

    func_id += token;

    halide_profiler_pipeline_stats *p_stats = NULL;
    for (halide_profiler_pipeline_stats *p = s->pipelines; p;
         p = (halide_profiler_pipeline_stats *)(p->next)) {
        // The same pipeline will deliver the same global constant
        // string, so they can be compared by pointer.
        if (p->name == pipeline_name) {
            p_stats = p;
            break;
        }
    }
    halide_assert(user_context, p_stats != NULL);
    halide_assert(user_context, (func_id - p_stats->first_func_id) >= 0);
    halide_assert(user_context, (func_id - p_stats->first_func_id) < p_stats->num_funcs);

    halide_profiler_func_stats *f_stats = &p_stats->funcs[func_id - p_stats->first_func_id];

    // Update per-pipeline memory stats
    p_stats->memory_current -= decr;

    // Update per-func memory stats
    f_stats->memory_current -= decr;

    /*// Update per-pipeline memory stats
    __sync_sub_and_fetch(&p_stats->memory_current, decr);

    // Update per-func memory stats
    __sync_sub_and_fetch(&f_stats->memory_current, decr);*/
}

WEAK void halide_profiler_report_unlocked(void *user_context, halide_profiler_state *s) {

    char line_buf[400];
    Printer<StringStreamPrinter, sizeof(line_buf)> sstr(user_context, line_buf);

    for (halide_profiler_pipeline_stats *p = s->pipelines; p;
         p = (halide_profiler_pipeline_stats *)(p->next)) {
        float t = p->time / 1000000.0f;
        if (!p->runs) continue;
        sstr.clear();
        int alloc_avg = 0;
        if (p->num_allocs != 0) {
            alloc_avg = p->memory_total/p->num_allocs;
        }
        sstr << p->name
             << "  total time: " << t << " ms"
             << "  samples: " << p->samples
             << "  runs: " << p->runs
             << "  time/run: " << t / p->runs << " ms"
             << "  num_allocs: " << p->num_allocs
             << "  mem_peak: " << p->memory_peak << " bytes"
             << "  mem_total: " << p->memory_total << " bytes"
             << "  alloc_avg: " << alloc_avg << " bytes\n";
        halide_print(user_context, sstr.str());
        if (p->time || p->memory_total) {
            for (int i = 0; i < p->num_funcs; i++) {
                sstr.clear();
                halide_profiler_func_stats *fs = p->funcs + i;

                // The first func is always a catch-all overhead
                // slot. Only report overhead time if it's non-zero
                if (i == 0 && fs->time == 0) continue;

                sstr << "  " << fs->name << ": ";
                while (sstr.size() < 25) sstr << " ";

                float ft = fs->time / (p->runs * 1000000.0f);
                sstr << ft << "ms";
                while (sstr.size() < 40) sstr << " ";

                int percent = 0;
                if (p->time != 0) {
                    percent = fs->time / (p->time / 100);
                }
                sstr << "(" << percent << "%)";
                while (sstr.size() < 55) sstr << " ";

                int alloc_avg = 0;
                if (fs->num_allocs != 0) {
                    alloc_avg = fs->memory_total/fs->num_allocs;
                }

                sstr << "(" << fs->memory_current << ", " << fs->memory_peak
                     << ", " << fs->memory_total << ", " << fs->num_allocs
                     << ", " << alloc_avg << ") bytes\n";

                halide_print(user_context, sstr.str());
            }
        }
    }
}

WEAK void halide_profiler_report(void *user_context) {
    halide_profiler_state *s = halide_profiler_get_state();
    ScopedMutexLock lock(&s->lock);
    halide_profiler_report_unlocked(user_context, s);
}

WEAK void halide_profiler_reset() {
    halide_profiler_state *s = halide_profiler_get_state();

    ScopedMutexLock lock(&s->lock);

    while (s->pipelines) {
        halide_profiler_pipeline_stats *p = s->pipelines;
        s->pipelines = (halide_profiler_pipeline_stats *)(p->next);
        free(p->funcs);
        free(p);
    }
    s->first_free_id = 0;
}

namespace {
__attribute__((destructor))
WEAK void halide_profiler_shutdown() {
    halide_profiler_state *s = halide_profiler_get_state();
    if (!s->started) return;
    s->current_func = halide_profiler_please_stop;
    do {
        // Memory barrier.
        __sync_synchronize(&s->started,
                           &s->current_func);
    } while (s->started);
    s->current_func = halide_profiler_outside_of_halide;

    // Print results. No need to lock anything because we just shut
    // down the thread.
    halide_profiler_report_unlocked(NULL, s);

    // Leak the memory. Not all implementations of ScopedMutexLock may
    // be safe to use at static destruction time (windows).
    // halide_profiler_reset();
}
}

WEAK void halide_profiler_pipeline_end(void *user_context, void *state) {
    ((halide_profiler_state *)state)->current_func = halide_profiler_outside_of_halide;
}

}
<commit_msg>Added temp fix for the profiler_allocate/free locking issues<commit_after>#include "runtime_internal.h"
#include "HalideRuntime.h"
#include "printer.h"
#include "scoped_mutex_lock.h"

// Note: The profiler thread may out-live any valid user_context, or
// be used across many different user_contexts, so nothing it calls
// can depend on the user context.

extern "C" {
// Returns the address of the global halide_profiler state
WEAK halide_profiler_state *halide_profiler_get_state() {
    static halide_profiler_state s = {{{0}}, NULL, 1, 0, 0, false};
    return &s;
}
}

namespace Halide { namespace Runtime { namespace Internal {

WEAK halide_profiler_pipeline_stats *find_or_create_pipeline(const char *pipeline_name, int num_funcs, const uint64_t *func_names) {
    halide_profiler_state *s = halide_profiler_get_state();

    for (halide_profiler_pipeline_stats *p = s->pipelines; p;
         p = (halide_profiler_pipeline_stats *)(p->next)) {
        // The same pipeline will deliver the same global constant
        // string, so they can be compared by pointer.
        if (p->name == pipeline_name &&
            p->num_funcs == num_funcs) {
            return p;
        }
    }
    // Create a new pipeline stats entry.
    halide_profiler_pipeline_stats *p =
        (halide_profiler_pipeline_stats *)malloc(sizeof(halide_profiler_pipeline_stats));
    if (!p) return NULL;
    p->next = s->pipelines;
    p->name = pipeline_name;
    p->first_func_id = s->first_free_id;
    p->num_funcs = num_funcs;
    p->runs = 0;
    p->time = 0;
    p->samples = 0;
    p->memory_current = 0;
    p->memory_peak = 0;
    p->memory_total = 0;
    p->num_allocs = 0;
    p->funcs = (halide_profiler_func_stats *)malloc(num_funcs * sizeof(halide_profiler_func_stats));
    if (!p->funcs) {
        free(p);
        return NULL;
    }
    for (int i = 0; i < num_funcs; i++) {
        p->funcs[i].time = 0;
        p->funcs[i].name = (const char *)(func_names[i]);
        p->funcs[i].memory_current = 0;
        p->funcs[i].memory_peak = 0;
        p->funcs[i].memory_total = 0;
        p->funcs[i].num_allocs = 0;
    }
    s->first_free_id += num_funcs;
    s->pipelines = p;
    return p;
}

WEAK void bill_func(halide_profiler_state *s, int func_id, uint64_t time) {
    halide_profiler_pipeline_stats *p_prev = NULL;
    for (halide_profiler_pipeline_stats *p = s->pipelines; p;
         p = (halide_profiler_pipeline_stats *)(p->next)) {
        if (func_id >= p->first_func_id && func_id < p->first_func_id + p->num_funcs) {
            if (p_prev) {
                // Bubble the pipeline to the top to speed up future queries.
                p_prev->next = (halide_profiler_pipeline_stats *)(p->next);
                p->next = s->pipelines;
                s->pipelines = p;
            }
            p->funcs[func_id - p->first_func_id].time += time;
            p->time += time;
            p->samples++;
            return;
        }
        p_prev = p;
    }
    // Someone must have called reset_state while a kernel was running. Do nothing.
}

WEAK void sampling_profiler_thread(void *) {
    halide_profiler_state *s = halide_profiler_get_state();

    // grab the lock
    halide_mutex_lock(&s->lock);

    while (s->current_func != halide_profiler_please_stop) {

        uint64_t t1 = halide_current_time_ns(NULL);
        uint64_t t = t1;
        while (1) {
            uint64_t t_now = halide_current_time_ns(NULL);
            int func = s->current_func;
            if (func == halide_profiler_please_stop) {
                break;
            } else if (func >= 0) {
                // Assume all time since I was last awake is due to
                // the currently running func.
                bill_func(s, func, t_now - t);
            }
            t = t_now;

            // Release the lock, sleep, reacquire.
            int sleep_ms = s->sleep_time;
            halide_mutex_unlock(&s->lock);
            halide_sleep_ms(NULL, sleep_ms);
            halide_mutex_lock(&s->lock);
        }
    }

    s->started = false;

    halide_mutex_unlock(&s->lock);
}

WEAK halide_profiler_pipeline_stats *find_pipeline_stats(const char *pipeline_name) {
    halide_profiler_state *s = halide_profiler_get_state();

    halide_profiler_pipeline_stats *p_stats = NULL;
    for (halide_profiler_pipeline_stats *p = s->pipelines; p;
         p = (halide_profiler_pipeline_stats *)(p->next)) {
        // The same pipeline will deliver the same global constant
        // string, so they can be compared by pointer.
        if (p->name == pipeline_name) {
            p_stats = p;
            break;
        }
    }
    return p_stats;
}

}}}

extern "C" {

// Returns a token identifying this pipeline instance.
WEAK int halide_profiler_pipeline_start(void *user_context,
                                        const char *pipeline_name,
                                        int num_funcs,
                                        const uint64_t *func_names) {
    halide_profiler_state *s = halide_profiler_get_state();

    ScopedMutexLock lock(&s->lock);

    if (!s->started) {
        halide_start_clock(user_context);
        halide_spawn_thread(user_context, sampling_profiler_thread, NULL);
        s->started = true;
    }

    halide_profiler_pipeline_stats *p =
        find_or_create_pipeline(pipeline_name, num_funcs, func_names);
    if (!p) {
        // Allocating space to track the statistics failed.
        return halide_error_out_of_memory(user_context);
    }
    p->runs++;

    return p->first_func_id;
}

WEAK void halide_profiler_memory_allocate(void *user_context,
                                          const char *pipeline_name,
                                          int token,
                                          int func_id,
                                          int incr) {
    halide_profiler_state *s = halide_profiler_get_state();

    func_id += token;

    halide_profiler_pipeline_stats *p_stats = find_pipeline_stats(pipeline_name);
    if (p_stats == NULL) {
        ScopedMutexLock lock(&s->lock);
        p_stats = find_pipeline_stats(pipeline_name);
    }

    halide_assert(user_context, p_stats != NULL);
    halide_assert(user_context, (func_id - p_stats->first_func_id) >= 0);
    halide_assert(user_context, (func_id - p_stats->first_func_id) < p_stats->num_funcs);

    halide_profiler_func_stats *f_stats = &p_stats->funcs[func_id - p_stats->first_func_id];

    // Update per-pipeline memory stats
    /*p_stats->num_allocs += 1;
    p_stats->memory_total += incr;
    p_stats->memory_current += incr;
    if (p_stats->memory_current > p_stats->memory_peak) {
        p_stats->memory_peak = p_stats->memory_current;
    }

    // Update per-func memory stats
    f_stats->num_allocs += 1;
    f_stats->memory_total += incr;
    f_stats->memory_current += incr;
    if (f_stats->memory_current > f_stats->memory_peak) {
        f_stats->memory_peak = f_stats->memory_current;
    }*/

    // Update per-pipeline memory stats
    __sync_add_and_fetch(&p_stats->num_allocs, 1);
    __sync_add_and_fetch(&p_stats->memory_total, incr);
    int p_mem_current = __sync_add_and_fetch(&p_stats->memory_current, incr);
    if (p_mem_current > p_stats->memory_peak) {
        p_stats->memory_peak = p_mem_current;
    }

    // Update per-func memory stats
    __sync_add_and_fetch(&f_stats->num_allocs, incr);
    __sync_add_and_fetch(&f_stats->memory_total, incr);
    int f_mem_current = __sync_add_and_fetch(&f_stats->memory_current, incr);
    if (f_mem_current > f_stats->memory_peak) {
        f_stats->memory_peak = f_mem_current;
    }
}

WEAK void halide_profiler_memory_free(void *user_context,
                                      const char *pipeline_name,
                                      int token,
                                      int func_id,
                                      int decr) {
    halide_profiler_state *s = halide_profiler_get_state();

    func_id += token;

    halide_profiler_pipeline_stats *p_stats = find_pipeline_stats(pipeline_name);
    if (p_stats == NULL) {
        ScopedMutexLock lock(&s->lock);
        p_stats = find_pipeline_stats(pipeline_name);
    }

    if (p_stats == NULL) {
        ScopedMutexLock lock(&s->lock);
        for (halide_profiler_pipeline_stats *p = s->pipelines; p;
             p = (halide_profiler_pipeline_stats *)(p->next)) {
            // The same pipeline will deliver the same global constant
            // string, so they can be compared by pointer.
            if (p->name == pipeline_name) {
                p_stats = p;
                break;
            }
        }
    }
    halide_assert(user_context, p_stats != NULL);
    halide_assert(user_context, (func_id - p_stats->first_func_id) >= 0);
    halide_assert(user_context, (func_id - p_stats->first_func_id) < p_stats->num_funcs);

    halide_profiler_func_stats *f_stats = &p_stats->funcs[func_id - p_stats->first_func_id];

    /*// Update per-pipeline memory stats
    p_stats->memory_current -= decr;

    // Update per-func memory stats
    f_stats->memory_current -= decr;*/

    // Update per-pipeline memory stats
    __sync_sub_and_fetch(&p_stats->memory_current, decr);

    // Update per-func memory stats
    __sync_sub_and_fetch(&f_stats->memory_current, decr);
}

WEAK void halide_profiler_report_unlocked(void *user_context, halide_profiler_state *s) {

    char line_buf[400];
    Printer<StringStreamPrinter, sizeof(line_buf)> sstr(user_context, line_buf);

    for (halide_profiler_pipeline_stats *p = s->pipelines; p;
         p = (halide_profiler_pipeline_stats *)(p->next)) {
        float t = p->time / 1000000.0f;
        if (!p->runs) continue;
        sstr.clear();
        int alloc_avg = 0;
        if (p->num_allocs != 0) {
            alloc_avg = p->memory_total/p->num_allocs;
        }
        sstr << p->name
             << "  total time: " << t << " ms"
             << "  samples: " << p->samples
             << "  runs: " << p->runs
             << "  time/run: " << t / p->runs << " ms"
             << "  num_allocs: " << p->num_allocs
             << "  mem_peak: " << p->memory_peak << " bytes"
             << "  mem_total: " << p->memory_total << " bytes"
             << "  alloc_avg: " << alloc_avg << " bytes\n";
        halide_print(user_context, sstr.str());
        if (p->time || p->memory_total) {
            for (int i = 0; i < p->num_funcs; i++) {
                sstr.clear();
                halide_profiler_func_stats *fs = p->funcs + i;

                // The first func is always a catch-all overhead
                // slot. Only report overhead time if it's non-zero
                if (i == 0 && fs->time == 0) continue;

                sstr << "  " << fs->name << ": ";
                while (sstr.size() < 25) sstr << " ";

                float ft = fs->time / (p->runs * 1000000.0f);
                sstr << ft << "ms";
                while (sstr.size() < 40) sstr << " ";

                int percent = 0;
                if (p->time != 0) {
                    percent = fs->time / (p->time / 100);
                }
                sstr << "(" << percent << "%)";
                while (sstr.size() < 55) sstr << " ";

                int alloc_avg = 0;
                if (fs->num_allocs != 0) {
                    alloc_avg = fs->memory_total/fs->num_allocs;
                }

                sstr << "(" << fs->memory_current << ", " << fs->memory_peak
                     << ", " << fs->memory_total << ", " << fs->num_allocs
                     << ", " << alloc_avg << ") bytes\n";

                halide_print(user_context, sstr.str());
            }
        }
    }
}

WEAK void halide_profiler_report(void *user_context) {
    halide_profiler_state *s = halide_profiler_get_state();
    ScopedMutexLock lock(&s->lock);
    halide_profiler_report_unlocked(user_context, s);
}

WEAK void halide_profiler_reset() {
    halide_profiler_state *s = halide_profiler_get_state();

    ScopedMutexLock lock(&s->lock);

    while (s->pipelines) {
        halide_profiler_pipeline_stats *p = s->pipelines;
        s->pipelines = (halide_profiler_pipeline_stats *)(p->next);
        free(p->funcs);
        free(p);
    }
    s->first_free_id = 0;
}

namespace {
__attribute__((destructor))
WEAK void halide_profiler_shutdown() {
    halide_profiler_state *s = halide_profiler_get_state();
    if (!s->started) return;
    s->current_func = halide_profiler_please_stop;
    do {
        // Memory barrier.
        __sync_synchronize(&s->started,
                           &s->current_func);
    } while (s->started);
    s->current_func = halide_profiler_outside_of_halide;

    // Print results. No need to lock anything because we just shut
    // down the thread.
    halide_profiler_report_unlocked(NULL, s);

    // Leak the memory. Not all implementations of ScopedMutexLock may
    // be safe to use at static destruction time (windows).
    // halide_profiler_reset();
}
}

WEAK void halide_profiler_pipeline_end(void *user_context, void *state) {
    ((halide_profiler_state *)state)->current_func = halide_profiler_outside_of_halide;
}

}
<|endoftext|>


<commit_before>#pragma once

#include <string>
#include <memory>

#include <cppconn/resultset.h>
#include <cppconn/prepared_statement.h>
#include <cppconn/connection.h>
#include <cppconn/exception.h>

#include <boost/optional.hpp>

#include "statement.hpp"

namespace rusql {
	struct NoResults : std::runtime_error {
		NoResults (std::string msg)
			: runtime_error (msg)
		{}

		NoResults()
			: runtime_error ("There were not results in your dataset.")
		{}
	};

	struct NoMoreResults : std::runtime_error {
		NoMoreResults (std::string msg)
			: runtime_error (msg)
		{}

		NoMoreResults()
			: runtime_error ("There were results, but we're out of those")
		{}
	};

	//! An interface for a result from a query. Incopyable.
	//! You can iterate over the rows from a query, or get them all at once.
	//! You can automatically convert a row to a boost::fusion'd struct, given that the column names are the same as the members, and the types are constructible from the values.
	struct ResultSet {
		template <typename T>
		ResultSet (T convertable_to_shared_ptr)
			: data (convertable_to_shared_ptr)
			, token (new Token) {
			next();
		}

		//! Resultsets go invalid when their connection is closed, or performed another query.
		bool is_valid() {
			return data->isClosed();
		}

		//! Invalidates the resultset, so that you can reuse the connection that was used to create this resultset. Use with caution.
		void release() {
			token.reset();
			data.reset();
		}

		uint64_t get_uint64 (size_t const index) {
			try {
				return data->getUInt64 (index);
			} catch (sql::InvalidArgumentException& e) {
				if (data->isBeforeFirst() || data->isAfterLast()) {
					if (data->rowsCount() == 0) {
						throw rusql::NoResults();
					} else {
						throw rusql::NoMoreResults();
					}
				} else {
					throw e;
				}
			}
		}

		uint64_t get_uint64 (std::string const column_name) {
			try {
				return data->getUInt64 (column_name);
			} catch (sql::InvalidArgumentException& e) {
				if (data->isBeforeFirst() || data->isAfterLast()) {
					if (data->rowsCount() == 0) {
						throw rusql::NoResults();
					} else {
						throw rusql::NoMoreResults();
					}
				} else {
					throw e;
				}
			}
		}

		std::string get_string (size_t const index) {
			try {
				return data->getString (index);
			} catch (sql::InvalidArgumentException& e) {
				if (data->isBeforeFirst() || data->isAfterLast()) {
					if (data->rowsCount() == 0) {
						throw rusql::NoResults();
					} else {
						throw rusql::NoMoreResults();
					}
				} else {
					throw e;
				}
			}
		}

		std::string get_string (std::string const column_name) {
			try {
				return data->getString (column_name);
			} catch (sql::InvalidArgumentException& e) {
				if (data->isBeforeFirst() || data->isAfterLast()) {
					if (data->rowsCount() == 0) {
						throw rusql::NoResults();
					} else {
						throw rusql::NoMoreResults();
					}
				} else {
					throw e;
				}
			}
		}

		bool is_null (size_t const index) {
			return data->isNull (index);
		}

		bool is_null (std::string const column_name) {
			return data->isNull (column_name);
		}

		bool is_valid() const {
			return ! (data->isBeforeFirst() || data->isAfterLast() || data->isClosed());
		}

		operator bool() const {
			return is_valid();
		}

		void next() {
			data->next();
		}

		struct Token {}; // What is shared between the connection and the resultset, so the connection knows when the resultset went out of scope.

		//! Returns a weak pointer that will expire if the ResultSet is released (goes out of scope, or release() is called.
		//! Note that this doesn't reflect if the resultset is still valid (due to underlying software). Use is_valid() for check that.
		std::weak_ptr<Token> get_token() {
			return token;
		}

	private:
		std::unique_ptr<sql::ResultSet> data;
		std::shared_ptr<Token> token;
	};

	struct PreparedStatement {
		template <typename ConvertableToShared>
		PreparedStatement (ConvertableToShared && statement)
			: statement (statement)
		{}

		ResultSet query() {
			return ResultSet (statement->executeQuery());
		}

		void execute() {
			statement->execute();
		}

		template <typename Head, typename ... T>
		PreparedStatement& bind (Head const& head, T const& ... values) {
			size_t index = 1;
			return bind_ (index, head, values ...);
		}

		PreparedStatement& bind() {
			return *this;
		}

		template <typename Head, typename ... T>
		PreparedStatement& bind_ (size_t& index, Head const& head, T const& ... tail) {
			bind_element (index, head);
			return bind_ (index, tail ...);
		}

		PreparedStatement& bind_ (size_t const) {
			return *this;
		}

		PreparedStatement& bind_element (size_t& position, std::string const& x) {
			statement->setString (position++, x);
			return *this;
		}

		PreparedStatement& bind_element (size_t& position, uint64_t const& x) {
			statement->setUInt64 (position++, x);
			return *this;
		}

		template <typename T>
		PreparedStatement& bind_element (size_t& position, boost::optional<T> const& x) {
			if (x) {
				bind_element (position, *x);
			}
			return *this;
		}
	private:
		std::unique_ptr<sql::PreparedStatement> statement;
	};

	struct Database;

	struct Connection {
		Connection (std::shared_ptr<Database> database);

		//! Reconnects if lost connection
		void make_valid() {
			if (!is_valid()) {
				connect();
			}
		}

		bool is_valid() {
			return !connection->isClosed();
		}

		//! Returns whether or not the connection is free to do an additional query i.e. there is not a resultset dependent on this connection anymore.
		bool is_free() {
			return result.expired();
		}

		ResultSet query (std::string const query) {
			make_valid();
			ResultSet set = prepare (query).query();
			result = set.get_token();
			return set;
		}

		PreparedStatement prepare (std::string const query) {
			make_valid();
			return PreparedStatement (connection->prepareStatement (query));
		}

	private:
		//! Connects with the database, disconnects the previous connection, if there was one.
		void connect();

		std::shared_ptr<Database> database;
		std::weak_ptr<ResultSet::Token> result;
		std::unique_ptr<sql::Connection> connection;
	};

	struct Database : std::enable_shared_from_this<Database> {
		struct ConstructionInfo {
			std::string host, user, password, database;

			ConstructionInfo (const std::string host, const std::string user, const std::string password, const std::string database)
				: host (host)
				, user (user)
				, password (password)
				, database (database)
			{}
		};

		Database (ConstructionInfo const& rh)
			: info (rh) {
		}

		Connection& get_connection() {
for (auto & c : connections) {
				if (c->is_free()) return *c;
			}

			return create_connection();
		}

		ResultSet query (std::string const query) {
			return get_connection().query (query);
		}

		template <typename ... T>
		ResultSet query (std::string const query, T const& ... args) {
			return get_connection().prepare (query).bind (args ...).query();
		}

		template <typename ... T>
		void execute (std::string const query, T const& ... args) {
			return get_connection().prepare (query).bind (args ...).execute();
		}

	private:
		friend struct Connection;
		ConstructionInfo const info;

		std::vector<std::shared_ptr<Connection>> connections;

		Connection& create_connection() {
			connections.emplace_back (std::make_shared<Connection> (shared_from_this()));
			return *connections.back();
		}
	};
}
<commit_msg>Fixes indentation<commit_after>#pragma once

#include <string>
#include <memory>

#include <cppconn/resultset.h>
#include <cppconn/prepared_statement.h>
#include <cppconn/connection.h>
#include <cppconn/exception.h>

#include <boost/optional.hpp>

#include "statement.hpp"

namespace rusql {
	struct NoResults : std::runtime_error {
		NoResults (std::string msg)
			: runtime_error (msg)
		{}

		NoResults()
			: runtime_error ("There were not results in your dataset.")
		{}
	};

	struct NoMoreResults : std::runtime_error {
		NoMoreResults (std::string msg)
			: runtime_error (msg)
		{}

		NoMoreResults()
			: runtime_error ("There were results, but we're out of those")
		{}
	};

	//! An interface for a result from a query. Incopyable.
	//! You can iterate over the rows from a query, or get them all at once.
	//! You can automatically convert a row to a boost::fusion'd struct, given that the column names are the same as the members, and the types are constructible from the values.
	struct ResultSet {
		template <typename T>
		ResultSet (T convertable_to_shared_ptr)
			: data (convertable_to_shared_ptr)
			, token (new Token) {
			next();
		}

		//! Resultsets go invalid when their connection is closed, or performed another query.
		bool is_valid() {
			return data->isClosed();
		}

		//! Invalidates the resultset, so that you can reuse the connection that was used to create this resultset. Use with caution.
		void release() {
			token.reset();
			data.reset();
		}

		uint64_t get_uint64 (size_t const index) {
			try {
				return data->getUInt64 (index);
			} catch (sql::InvalidArgumentException& e) {
				if (data->isBeforeFirst() || data->isAfterLast()) {
					if (data->rowsCount() == 0) {
						throw rusql::NoResults();
					} else {
						throw rusql::NoMoreResults();
					}
				} else {
					throw e;
				}
			}
		}

		uint64_t get_uint64 (std::string const column_name) {
			try {
				return data->getUInt64 (column_name);
			} catch (sql::InvalidArgumentException& e) {
				if (data->isBeforeFirst() || data->isAfterLast()) {
					if (data->rowsCount() == 0) {
						throw rusql::NoResults();
					} else {
						throw rusql::NoMoreResults();
					}
				} else {
					throw e;
				}
			}
		}

		std::string get_string (size_t const index) {
			try {
				return data->getString (index);
			} catch (sql::InvalidArgumentException& e) {
				if (data->isBeforeFirst() || data->isAfterLast()) {
					if (data->rowsCount() == 0) {
						throw rusql::NoResults();
					} else {
						throw rusql::NoMoreResults();
					}
				} else {
					throw e;
				}
			}
		}

		std::string get_string (std::string const column_name) {
			try {
				return data->getString (column_name);
			} catch (sql::InvalidArgumentException& e) {
				if (data->isBeforeFirst() || data->isAfterLast()) {
					if (data->rowsCount() == 0) {
						throw rusql::NoResults();
					} else {
						throw rusql::NoMoreResults();
					}
				} else {
					throw e;
				}
			}
		}

		bool is_null (size_t const index) {
			return data->isNull (index);
		}

		bool is_null (std::string const column_name) {
			return data->isNull (column_name);
		}

		bool is_valid() const {
			return ! (data->isBeforeFirst() || data->isAfterLast() || data->isClosed());
		}

		operator bool() const {
			return is_valid();
		}

		void next() {
			data->next();
		}

		struct Token {}; // What is shared between the connection and the resultset, so the connection knows when the resultset went out of scope.

		//! Returns a weak pointer that will expire if the ResultSet is released (goes out of scope, or release() is called.
		//! Note that this doesn't reflect if the resultset is still valid (due to underlying software). Use is_valid() for check that.
		std::weak_ptr<Token> get_token() {
			return token;
		}

	private:
		std::unique_ptr<sql::ResultSet> data;
		std::shared_ptr<Token> token;
	};

	struct PreparedStatement {
		template <typename ConvertableToShared>
		PreparedStatement (ConvertableToShared && statement)
			: statement (statement)
		{}

		ResultSet query() {
			return ResultSet (statement->executeQuery());
		}

		void execute() {
			statement->execute();
		}

		template <typename Head, typename ... T>
		PreparedStatement& bind (Head const& head, T const& ... values) {
			size_t index = 1;
			return bind_ (index, head, values ...);
		}

		PreparedStatement& bind() {
			return *this;
		}

		template <typename Head, typename ... T>
		PreparedStatement& bind_ (size_t& index, Head const& head, T const& ... tail) {
			bind_element (index, head);
			return bind_ (index, tail ...);
		}

		PreparedStatement& bind_ (size_t const) {
			return *this;
		}

		PreparedStatement& bind_element (size_t& position, std::string const& x) {
			statement->setString (position++, x);
			return *this;
		}

		PreparedStatement& bind_element (size_t& position, uint64_t const& x) {
			statement->setUInt64 (position++, x);
			return *this;
		}

		template <typename T>
		PreparedStatement& bind_element (size_t& position, boost::optional<T> const& x) {
			if (x) {
				bind_element (position, *x);
			}
			return *this;
		}
	private:
		std::unique_ptr<sql::PreparedStatement> statement;
	};

	struct Database;

	struct Connection {
		Connection (std::shared_ptr<Database> database);

		//! Reconnects if lost connection
		void make_valid() {
			if (!is_valid()) {
				connect();
			}
		}

		bool is_valid() {
			return !connection->isClosed();
		}

		//! Returns whether or not the connection is free to do an additional query i.e. there is not a resultset dependent on this connection anymore.
		bool is_free() {
			return result.expired();
		}

		ResultSet query (std::string const query) {
			make_valid();
			ResultSet set = prepare (query).query();
			result = set.get_token();
			return set;
		}

		PreparedStatement prepare (std::string const query) {
			make_valid();
			return PreparedStatement (connection->prepareStatement (query));
		}

	private:
		//! Connects with the database, disconnects the previous connection, if there was one.
		void connect();

		std::shared_ptr<Database> database;
		std::weak_ptr<ResultSet::Token> result;
		std::unique_ptr<sql::Connection> connection;
	};

	struct Database : std::enable_shared_from_this<Database> {
		struct ConstructionInfo {
			std::string host, user, password, database;

			ConstructionInfo (const std::string host, const std::string user, const std::string password, const std::string database)
				: host (host)
				, user (user)
				, password (password)
				, database (database)
			{}
		};

		Database (ConstructionInfo const& rh)
			: info (rh) {
		}

		Connection& get_connection() {
			for (auto & c : connections) {
				if (c->is_free()) return *c;
			}

			return create_connection();
		}

		ResultSet query (std::string const query) {
			return get_connection().query (query);
		}

		template <typename ... T>
		ResultSet query (std::string const query, T const& ... args) {
			return get_connection().prepare (query).bind (args ...).query();
		}

		template <typename ... T>
		void execute (std::string const query, T const& ... args) {
			return get_connection().prepare (query).bind (args ...).execute();
		}

	private:
		friend struct Connection;
		ConstructionInfo const info;

		std::vector<std::shared_ptr<Connection>> connections;

		Connection& create_connection() {
			connections.emplace_back (std::make_shared<Connection> (shared_from_this()));
			return *connections.back();
		}
	};
}
<|endoftext|>


<commit_before>//===--- CGTemporaries.cpp - Emit LLVM Code for C++ temporaries -----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This contains code dealing with C++ code generation of temporaries
//
//===----------------------------------------------------------------------===//

#include "CodeGenFunction.h"
using namespace clang;
using namespace CodeGen;

void CodeGenFunction::PushCXXTemporary(const CXXTemporary *Temporary,
                                       llvm::Value *Ptr) {
  llvm::BasicBlock *DtorBlock = createBasicBlock("temp.dtor");

  llvm::Value *CondPtr = 0;

  // Check if temporaries need to be conditional. If so, we'll create a
  // condition boolean, initialize it to 0 and
  if (ConditionalBranchLevel != 0) {
    CondPtr = CreateTempAlloca(llvm::Type::getInt1Ty(VMContext), "cond");

    // Initialize it to false. This initialization takes place right after
    // the alloca insert point.
    llvm::StoreInst *SI =
      new llvm::StoreInst(llvm::ConstantInt::getFalse(VMContext), CondPtr);
    llvm::BasicBlock *Block = AllocaInsertPt->getParent();
    Block->getInstList().insertAfter((llvm::Instruction *)AllocaInsertPt, SI);

    // Now set it to true.
    Builder.CreateStore(llvm::ConstantInt::getTrue(VMContext), CondPtr);
  }

  LiveTemporaries.push_back(CXXLiveTemporaryInfo(Temporary, Ptr, DtorBlock,
                                                 CondPtr));

  PushCleanupBlock(DtorBlock);

  if (Exceptions) {
    const CXXLiveTemporaryInfo& Info = LiveTemporaries.back();
    llvm::BasicBlock *CondEnd = 0;

    EHCleanupBlock Cleanup(*this);

    // If this is a conditional temporary, we need to check the condition
    // boolean and only call the destructor if it's true.
    if (Info.CondPtr) {
      llvm::BasicBlock *CondBlock = createBasicBlock("cond.dtor.call");
      CondEnd = createBasicBlock("cond.dtor.end");

      llvm::Value *Cond = Builder.CreateLoad(Info.CondPtr);
      Builder.CreateCondBr(Cond, CondBlock, CondEnd);
      EmitBlock(CondBlock);
    }

    EmitCXXDestructorCall(Info.Temporary->getDestructor(),
                          Dtor_Complete, Info.ThisPtr);

    if (CondEnd) {
      // Reset the condition. to false.
      Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), Info.CondPtr);
      EmitBlock(CondEnd);
    }
  }
}

void CodeGenFunction::PopCXXTemporary() {
  const CXXLiveTemporaryInfo& Info = LiveTemporaries.back();

  CleanupBlockInfo CleanupInfo = PopCleanupBlock();
  assert(CleanupInfo.CleanupBlock == Info.DtorBlock &&
         "Cleanup block mismatch!");
  assert(!CleanupInfo.SwitchBlock &&
         "Should not have a switch block for temporary cleanup!");
  assert(!CleanupInfo.EndBlock &&
         "Should not have an end block for temporary cleanup!");

  llvm::BasicBlock *CurBB = Builder.GetInsertBlock();
  if (CurBB && !CurBB->getTerminator() &&
      Info.DtorBlock->getNumUses() == 0) {
    CurBB->getInstList().splice(CurBB->end(), Info.DtorBlock->getInstList());
    delete Info.DtorBlock;
  } else
    EmitBlock(Info.DtorBlock);

  llvm::BasicBlock *CondEnd = 0;

  // If this is a conditional temporary, we need to check the condition
  // boolean and only call the destructor if it's true.
  if (Info.CondPtr) {
    llvm::BasicBlock *CondBlock = createBasicBlock("cond.dtor.call");
    CondEnd = createBasicBlock("cond.dtor.end");

    llvm::Value *Cond = Builder.CreateLoad(Info.CondPtr);
    Builder.CreateCondBr(Cond, CondBlock, CondEnd);
    EmitBlock(CondBlock);
  }

  EmitCXXDestructorCall(Info.Temporary->getDestructor(),
                        Dtor_Complete, Info.ThisPtr);

  if (CondEnd) {
    // Reset the condition. to false.
    Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), Info.CondPtr);
    EmitBlock(CondEnd);
  }

  LiveTemporaries.pop_back();
}

RValue
CodeGenFunction::EmitCXXExprWithTemporaries(const CXXExprWithTemporaries *E,
                                            llvm::Value *AggLoc,
                                            bool IsAggLocVolatile,
                                            bool IsInitializer) {
  // Keep track of the current cleanup stack depth.
  size_t CleanupStackDepth = CleanupEntries.size();
  (void) CleanupStackDepth;

  unsigned OldNumLiveTemporaries = LiveTemporaries.size();

  RValue RV = EmitAnyExpr(E->getSubExpr(), AggLoc, IsAggLocVolatile,
                          /*IgnoreResult=*/false, IsInitializer);

  // Pop temporaries.
  while (LiveTemporaries.size() > OldNumLiveTemporaries)
    PopCXXTemporary();

  assert(CleanupEntries.size() == CleanupStackDepth &&
         "Cleanup size mismatch!");

  return RV;
}

LValue CodeGenFunction::EmitCXXExprWithTemporariesLValue(
                                              const CXXExprWithTemporaries *E) {
  // Keep track of the current cleanup stack depth.
  size_t CleanupStackDepth = CleanupEntries.size();
  (void) CleanupStackDepth;

  unsigned OldNumLiveTemporaries = LiveTemporaries.size();

  LValue LV = EmitLValue(E->getSubExpr());

  // Pop temporaries.
  while (LiveTemporaries.size() > OldNumLiveTemporaries)
    PopCXXTemporary();

  assert(CleanupEntries.size() == CleanupStackDepth &&
         "Cleanup size mismatch!");

  return LV;
}
<commit_msg>Assert that we aren't trying to push the same C++ temporary onto the live temporary stack twice. A little insurance against PR5867 surprising us again<commit_after>//===--- CGTemporaries.cpp - Emit LLVM Code for C++ temporaries -----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This contains code dealing with C++ code generation of temporaries
//
//===----------------------------------------------------------------------===//

#include "CodeGenFunction.h"
using namespace clang;
using namespace CodeGen;

void CodeGenFunction::PushCXXTemporary(const CXXTemporary *Temporary,
                                       llvm::Value *Ptr) {
  assert((LiveTemporaries.empty() ||
          LiveTemporaries.back().ThisPtr != Ptr) &&
         "Pushed the same temporary twice; AST is likely wrong");
  llvm::BasicBlock *DtorBlock = createBasicBlock("temp.dtor");

  llvm::Value *CondPtr = 0;

  // Check if temporaries need to be conditional. If so, we'll create a
  // condition boolean, initialize it to 0 and
  if (ConditionalBranchLevel != 0) {
    CondPtr = CreateTempAlloca(llvm::Type::getInt1Ty(VMContext), "cond");

    // Initialize it to false. This initialization takes place right after
    // the alloca insert point.
    llvm::StoreInst *SI =
      new llvm::StoreInst(llvm::ConstantInt::getFalse(VMContext), CondPtr);
    llvm::BasicBlock *Block = AllocaInsertPt->getParent();
    Block->getInstList().insertAfter((llvm::Instruction *)AllocaInsertPt, SI);

    // Now set it to true.
    Builder.CreateStore(llvm::ConstantInt::getTrue(VMContext), CondPtr);
  }

  LiveTemporaries.push_back(CXXLiveTemporaryInfo(Temporary, Ptr, DtorBlock,
                                                 CondPtr));

  PushCleanupBlock(DtorBlock);

  if (Exceptions) {
    const CXXLiveTemporaryInfo& Info = LiveTemporaries.back();
    llvm::BasicBlock *CondEnd = 0;

    EHCleanupBlock Cleanup(*this);

    // If this is a conditional temporary, we need to check the condition
    // boolean and only call the destructor if it's true.
    if (Info.CondPtr) {
      llvm::BasicBlock *CondBlock = createBasicBlock("cond.dtor.call");
      CondEnd = createBasicBlock("cond.dtor.end");

      llvm::Value *Cond = Builder.CreateLoad(Info.CondPtr);
      Builder.CreateCondBr(Cond, CondBlock, CondEnd);
      EmitBlock(CondBlock);
    }

    EmitCXXDestructorCall(Info.Temporary->getDestructor(),
                          Dtor_Complete, Info.ThisPtr);

    if (CondEnd) {
      // Reset the condition. to false.
      Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), Info.CondPtr);
      EmitBlock(CondEnd);
    }
  }
}

void CodeGenFunction::PopCXXTemporary() {
  const CXXLiveTemporaryInfo& Info = LiveTemporaries.back();

  CleanupBlockInfo CleanupInfo = PopCleanupBlock();
  assert(CleanupInfo.CleanupBlock == Info.DtorBlock &&
         "Cleanup block mismatch!");
  assert(!CleanupInfo.SwitchBlock &&
         "Should not have a switch block for temporary cleanup!");
  assert(!CleanupInfo.EndBlock &&
         "Should not have an end block for temporary cleanup!");

  llvm::BasicBlock *CurBB = Builder.GetInsertBlock();
  if (CurBB && !CurBB->getTerminator() &&
      Info.DtorBlock->getNumUses() == 0) {
    CurBB->getInstList().splice(CurBB->end(), Info.DtorBlock->getInstList());
    delete Info.DtorBlock;
  } else
    EmitBlock(Info.DtorBlock);

  llvm::BasicBlock *CondEnd = 0;

  // If this is a conditional temporary, we need to check the condition
  // boolean and only call the destructor if it's true.
  if (Info.CondPtr) {
    llvm::BasicBlock *CondBlock = createBasicBlock("cond.dtor.call");
    CondEnd = createBasicBlock("cond.dtor.end");

    llvm::Value *Cond = Builder.CreateLoad(Info.CondPtr);
    Builder.CreateCondBr(Cond, CondBlock, CondEnd);
    EmitBlock(CondBlock);
  }

  EmitCXXDestructorCall(Info.Temporary->getDestructor(),
                        Dtor_Complete, Info.ThisPtr);

  if (CondEnd) {
    // Reset the condition. to false.
    Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), Info.CondPtr);
    EmitBlock(CondEnd);
  }

  LiveTemporaries.pop_back();
}

RValue
CodeGenFunction::EmitCXXExprWithTemporaries(const CXXExprWithTemporaries *E,
                                            llvm::Value *AggLoc,
                                            bool IsAggLocVolatile,
                                            bool IsInitializer) {
  // Keep track of the current cleanup stack depth.
  size_t CleanupStackDepth = CleanupEntries.size();
  (void) CleanupStackDepth;

  unsigned OldNumLiveTemporaries = LiveTemporaries.size();

  RValue RV = EmitAnyExpr(E->getSubExpr(), AggLoc, IsAggLocVolatile,
                          /*IgnoreResult=*/false, IsInitializer);

  // Pop temporaries.
  while (LiveTemporaries.size() > OldNumLiveTemporaries)
    PopCXXTemporary();

  assert(CleanupEntries.size() == CleanupStackDepth &&
         "Cleanup size mismatch!");

  return RV;
}

LValue CodeGenFunction::EmitCXXExprWithTemporariesLValue(
                                              const CXXExprWithTemporaries *E) {
  // Keep track of the current cleanup stack depth.
  size_t CleanupStackDepth = CleanupEntries.size();
  (void) CleanupStackDepth;

  unsigned OldNumLiveTemporaries = LiveTemporaries.size();

  LValue LV = EmitLValue(E->getSubExpr());

  // Pop temporaries.
  while (LiveTemporaries.size() > OldNumLiveTemporaries)
    PopCXXTemporary();

  assert(CleanupEntries.size() == CleanupStackDepth &&
         "Cleanup size mismatch!");

  return LV;
}
<|endoftext|>