LLVM小结

LLVM小结

 

如果说gcc是FSF的传奇,llvm就是Chris Lattner的小清新。当然啦,想具体看看这位四处游山玩水还GPA 4.0的大神和他的LLVM编译链还有他与苹果之间的故事的读者可以移步http://news.cnblogs.com/n/127343/。另外,据悉,FreeBSD自10.0开始将会完全采用llvm编译链编译,而之前的版本,与Linux一样,都是采用的gcc编译的。

以上,就算是“拉大旗扯虎皮”,既是给llvm做个简介,也是让大家知道本篇博文还是说的是比较“有用的东西,而不是什么虚无飘渺的东西。

llvm在编译链中的环节其实是属于后端,前端可以采用gcc或者clang(从某些编译课程来看,用yacc和bison写的前端也行)。gcc想必大家都很熟悉,我也就不再赘述。clang则是专门为llvm定制的前端,据说当初开发它的一个很重要的因素就是gcc和IDE配合得不太好,而且gcc模块之间写得比较混杂,难以修改。作为前端,虽然我没有使用过配合clang的IDE(Apple developer能使用到的XCode算一个,不过我还没真正用过),但确实,在终端下调试的时候clang给出的输出比gcc的要好理解得多。不过clang是名副其实的CLang,只支持C/Obj-C/C++这三种语言;而gcc当然是无所不包无所不能的了。这里我上个C程序的例子吧(如果觉得不过瘾,你可以自行尝试C++,据说C++的类模板最能体现这两者的区别,也可以直接去http://blog.llvm.org/2010/04/amazing-feats-of-clang-error-recovery.html):

复制代码
1 #include <stdio.h>
2 
3 int main(){
4     return 1? 2 3
5 }
复制代码

我们来看看clang和gcc给出的出错信息分别是什么(为了证实确实是这段程序,我先cat了它):

首先,其实gcc没有给全出错信息,如果你只照gcc的输出在第4行14列添加了冒号,那么你再次编译的时候它还会告诉你这一行的末尾缺分号;而clang不仅给全了错误而且每个错误都是出错信息一行,源代码一行,建议一行(note也是这样的),并且输出有色彩分别。笔者的gcc版本是4.7.3,clang版本是3.2-1,都是Ubuntu 13.04下最新的(顺带插一句:不知道为什么llvm 3.3已经发布很久了,Ubuntu还没有正式采用)。我觉得前端有一个很重要的功能就是报错,clang做得很好,至少这点比gcc做得好。苹果的产品在“看得见”的地方一向都做得很好,clang也是这样的。

编译器的前端,不仅要能生成AST,而且如果源代码有语法错误或者警告(虽然程序猿貌似都忽略警告,但实际上有的时候警告还是很有用的)要应当能给我们很好的指出,毕竟我们是人不是机器,要人性化一点;另外,后端又要能够在前端分析出的AST为程序做出极致的优化,当然,我不是说程序优化就靠编译器了,程序猿本身就应当编写出好的代码,但是编译器确实应当负责优化程序。llvm和gcc到底谁优化得更好,大家说法不一,给出的数据差距也很大,但是呢,不过呢,“LLVM has been awarded the 2012 ACM Software System Award!”,估计肯定是不会差的了。除了各种天花乱坠的数据外(如果非要看的话,我还是给个早年的数据吧:http://llvm.org/pubs/2007-07-25-LLVM-2.0-and-Beyond.pdf),毕竟程序最终是要给用户用的,毕竟用户体验才是最重要的,所以我们直接比较产品。举个栗子:LLVMpipe,这是一个类似于即时编译或者说在线编译的东西,它将OpenGL的代码(本来是抛给GPU的)编译为CPU可执行的代码并交由CPU执行。这个东西被很多支持老旧电脑的Linux发行版所采用,所以想来效率也不会太差。当然,效率高不高和运行快不快其实并不等价,因为运行快不快和具体的硬件环境还是有很大关系的。

说到编译优化,llvm似乎还有一个奇怪的优化方法:llvm(low level virtual machine)本身就是一种抽象的、虚拟的计算机架构,其特性介于RISC和CISC之间,llvm会先将代码编译为llvm架构的字节码(这里还是说说数据吧,从其官方数据来看,生成的字节码略多于x86的目标代码而少于SPARC的目标代码),然后可以对字节码进行JIT优化然后再翻译为目标架构的二进制代码。另外,llvm实际上采用的是一种全生命周期(lifelong)的优化策略(虽然还是很偏重静态优化),最直接的体现就是比起gcc能做到的 -O3,llvm可以做到 -O4,而且 -O4采用的是LLVMgold.so所提供的运行时库。llvm本身的设计思想就是希望做到编译时、链接时、运行时、空闲时的全方位优化。关于这些优化,在llvm官网可以找到,请移步http://llvm.org/pubs/2004-01-30-CGO-LLVM.html

最后来说一下llvm和gcc的兼容性,我前面只是说了llvm的后端兼容gcc的前端生成的AST,其实llvm对gcc的兼容性是很高的,我现在系统的环境变量CC设置的就是clang,我编译了很多工程都不用改Makefile就可以成功编译,不过很多工程里只是采用的 -O2,让我略不爽,所以我就手动将之改为 -O4,当然也就需要修改一下CFLAGS和LDFLAGS,为之加上llvm-config的输出。llvm本身在脚本上就设计为和gcc的兼容,所以改换编译链十分容易。

 

开源库CImg 数据格式存储之二(RGB 顺序)

 

在上一篇博客中已经初步说明了GDI和CImg数据的存储格式感谢博友 Imageshop 评论说明

CImg的说明文档中已有详细说明(详见上篇博客说明)

CImg的数据格式确实是RRRGGGBBB顺序存储的已经毫无疑问,但是其参考手册中对其他GDI

的数据格式说明是略有瑕疵,参考手册说其他GDI的数据格式是RGBRGBRGB,其实则不是经过验证

bmp类型的数据格式应该是BGRBGRBGR 下面用code验证

说明:使用MFC 同时用CImage和CImg加载同一幅图片

复制代码
void ImageIO::loadImage(const BiCImg & image, T*& pImagePlane,int& width,int& height,int& nchannels)
{
    // get the image information
    
    width=image.width();
    height=image.height();
    nchannels=3;
    int rgb_leng=width*height;
    pImagePlane=new T[width*height*nchannels];

    // check whether the type is float point
    bool IsFloat=false;
    if(typeid(T)==typeid(double) || typeid(T)==typeid(float) || typeid(T)==typeid(long double))
        IsFloat=true;
    
    const unsigned char* plinebuffer;
    plinebuffer=image.data(0,0);
    for(int i=0;i<height;i++)
    {        
        //plinebuffer=image.scanLine(i);
        for(int j=0;j<width;j++)
        {
                        
pImagePlane[(i*width+j)*3]=plinebuffer[i*width+j+2*rgb_leng];//RGB b
                pImagePlane[(i*width+j)*3+1]=plinebuffer[i*width+j+rgb_leng];//RGB g
                pImagePlane[(i*width+j)*3+2]=plinebuffer[i*width+j];//RGB r
            
        }
    }
}
复制代码

上述为正确的顺序,若改为如下代码

                pImagePlane[(i*width+j)*3]=plinebuffer[i*width+j];//RGB r 

                pImagePlane[(i*width+j)*3+1]=plinebuffer[i*width+j+rgb_leng];//RGB g

           pImagePlane[(i*width+j)*3+2]=plinebuffer[i*width+j+2*rgb_leng];//RGB b

实验效果如下图

右图为原始图片,明显左图的蓝色部分取代了原始图片的红色应该是BGR缺写成了RGB 

 
 
 
 
 
原文地址:https://www.cnblogs.com/Leo_wl/p/3271572.html