Archive for 3月 17th, 2013

  1. Hello, COM(LLVM) World!

    Posted on 3月 17th, 2013 by cx20

    COM(LLVM)

    COM(Component Object Model)はマイクロソフトの提唱するプログラム部品の仕様である。
    COM を用いて開発された部品であれば言語を問わず利用することができる。
    以下は LLVM Assembler による COM クライアントの例となっている。

    ソースコード

    ; ModuleID = 'hello.c'
    target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f80:128:128-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S32"
    target triple = "i686-w64-mingw32"
     
    %struct._GUID = type { i32, i16, i16, [8 x i8] }
    %struct.IDispatch = type { %struct.IDispatchVtbl* }
    %struct.IDispatchVtbl = type { i32 (%struct.IDispatch*, %struct._GUID*, i8**)*, i32 (%struct.IDispatch*)*, i32 (%struct.IDispatch*)*, i32 (%struct.IDispatch*, i32*)*, i32 (%struct.IDispatch*, i32, i32, %struct.ITypeInfo**)*, i32 (%struct.IDispatch*, %struct._GUID*, i16**, i32, i32, i32*)*, i32 (%struct.IDispatch*, i32, %struct._GUID*, i32, i16, %struct.tagDISPPARAMS*, %struct.tagVARIANT*, %struct.tagEXCEPINFO*, i32*)* }
    %struct.ITypeInfo = type { %struct.ITypeInfoVtbl* }
    %struct.ITypeInfoVtbl = type { i32 (%struct.ITypeInfo*, %struct._GUID*, i8**)*, i32 (%struct.ITypeInfo*)*, i32 (%struct.ITypeInfo*)*, i32 (%struct.ITypeInfo*, %struct.tagTYPEATTR**)*, i32 (%struct.ITypeInfo*, %struct.ITypeComp**)*, i32 (%struct.ITypeInfo*, i32, %struct.tagFUNCDESC**)*, i32 (%struct.ITypeInfo*, i32, %struct.tagVARDESC**)*, i32 (%struct.ITypeInfo*, i32, i16**, i32, i32*)*, i32 (%struct.ITypeInfo*, i32, i32*)*, i32 (%struct.ITypeInfo*, i32, i32*)*, i32 (%struct.ITypeInfo*, i16**, i32, i32*)*, i32 (%struct.ITypeInfo*, i8*, i32, i16, %struct.tagDISPPARAMS*, %struct.tagVARIANT*, %struct.tagEXCEPINFO*, i32*)*, i32 (%struct.ITypeInfo*, i32, i16**, i16**, i32*, i16**)*, i32 (%struct.ITypeInfo*, i32, i32, i16**, i16**, i16*)*, i32 (%struct.ITypeInfo*, i32, %struct.ITypeInfo**)*, i32 (%struct.ITypeInfo*, i32, i32, i8**)*, i32 (%struct.ITypeInfo*, %struct.IUnknown*, %struct._GUID*, i8**)*, i32 (%struct.ITypeInfo*, i32, i16**)*, i32 (%struct.ITypeInfo*, %struct.ITypeLib**, i32*)*, void (%struct.ITypeInfo*, %struct.tagTYPEATTR*)*, void (%struct.ITypeInfo*, %struct.tagFUNCDESC*)*, void (%struct.ITypeInfo*, %struct.tagVARDESC*)* }
    %struct.tagTYPEATTR = type <{ %struct._GUID, i32, i32, i32, i32, i16*, i32, i32, i16, i16, i16, i16, i16, i16, i16, i16, %struct.tagTYPEDESC, %struct.tagIDLDESC }>
    %struct.tagTYPEDESC = type <{ %union.anon, i16, [2 x i8] }>
    %union.anon = type { %struct.tagTYPEDESC* }
    %struct.tagIDLDESC = type { i32, i16 }
    %struct.ITypeComp = type { %struct.ITypeCompVtbl* }
    %struct.ITypeCompVtbl = type { i32 (%struct.ITypeComp*, %struct._GUID*, i8**)*, i32 (%struct.ITypeComp*)*, i32 (%struct.ITypeComp*)*, i32 (%struct.ITypeComp*, i16*, i32, i16, %struct.ITypeInfo**, i32*, %union.tagBINDPTR*)*, i32 (%struct.ITypeComp*, i16*, i32, %struct.ITypeInfo**, %struct.ITypeComp**)* }
    %union.tagBINDPTR = type { %struct.tagFUNCDESC* }
    %struct.tagFUNCDESC = type <{ i32, i32*, %struct.tagELEMDESC*, i32, i32, i32, i16, i16, i16, i16, %struct.tagELEMDESC, i16, [2 x i8] }>
    %struct.tagELEMDESC = type <{ %struct.tagTYPEDESC, %union.anon.0 }>
    %union.anon.0 = type { %struct.tagIDLDESC }
    %struct.tagVARDESC = type <{ i32, i16*, %union.anon.9, %struct.tagELEMDESC, i16, [2 x i8], i32 }>
    %union.anon.9 = type { i32 }
    %struct.tagDISPPARAMS = type { %struct.tagVARIANT*, i32*, i32, i32 }
    %struct.tagVARIANT = type { %union.anon.1 }
    %union.anon.1 = type { %struct.anon }
    %struct.anon = type { i16, i16, i16, i16, %union.anon.2 }
    %union.anon.2 = type { i64 }
    %struct.tagEXCEPINFO = type { i16, i16, i16*, i16*, i16*, i32, i8*, i32 (%struct.tagEXCEPINFO*)*, i32 }
    %struct.IUnknown = type { %struct.IUnknownVtbl* }
    %struct.IUnknownVtbl = type { i32 (%struct.IUnknown*, %struct._GUID*, i8**)*, i32 (%struct.IUnknown*)*, i32 (%struct.IUnknown*)* }
    %struct.ITypeLib = type { %struct.ITypeLibVtbl* }
    %struct.ITypeLibVtbl = type { i32 (%struct.ITypeLib*, %struct._GUID*, i8**)*, i32 (%struct.ITypeLib*)*, i32 (%struct.ITypeLib*)*, i32 (%struct.ITypeLib*)*, i32 (%struct.ITypeLib*, i32, %struct.ITypeInfo**)*, i32 (%struct.ITypeLib*, i32, i32*)*, i32 (%struct.ITypeLib*, %struct._GUID*, %struct.ITypeInfo**)*, i32 (%struct.ITypeLib*, %struct.tagTLIBATTR**)*, i32 (%struct.ITypeLib*, %struct.ITypeComp**)*, i32 (%struct.ITypeLib*, i32, i16**, i16**, i32*, i16**)*, i32 (%struct.ITypeLib*, i16*, i32, i32*)*, i32 (%struct.ITypeLib*, i16*, i32, %struct.ITypeInfo**, i32*, i16*)*, void (%struct.ITypeLib*, %struct.tagTLIBATTR*)* }
    %struct.tagTLIBATTR = type <{ %struct._GUID, i32, i32, i16, i16, i16, [2 x i8] }>
     
    @.str = private unnamed_addr constant [16 x i16] [i16 66, i16 114, i16 111, i16 119, i16 115, i16 101, i16 70, i16 111, i16 114, i16 70, i16 111, i16 108, i16 100, i16 101, i16 114, i16 0], align 2
    @.str1 = private unnamed_addr constant [18 x i16] [i16 83, i16 104, i16 101, i16 108, i16 108, i16 46, i16 65, i16 112, i16 112, i16 108, i16 105, i16 99, i16 97, i16 116, i16 105, i16 111, i16 110, i16 0], align 2
    @IID_IDispatch = external constant %struct._GUID
    @GUID_NULL = external constant %struct._GUID
    @.str2 = private unnamed_addr constant [18 x i16] [i16 72, i16 101, i16 108, i16 108, i16 111, i16 44, i16 32, i16 67, i16 79, i16 77, i16 32, i16 87, i16 111, i16 114, i16 108, i16 100, i16 33, i16 0], align 2
     
    define i32 @main(i32 %argc, i8** nocapture %argv) nounwind {
      %clsid = alloca %struct._GUID, align 4
      %pShell = alloca %struct.IDispatch*, align 4
      %dispid = alloca i32, align 4
      %ptName = alloca i16*, align 4
      %param = alloca %struct.tagDISPPARAMS, align 4
      %varg = alloca [4 x %struct.tagVARIANT], align 8
      %vResult = alloca %struct.tagVARIANT, align 8
      store i16* getelementptr inbounds ([16 x i16]* @.str, i32 0, i32 0), i16** %ptName, align 4, !tbaa !0
      %1 = bitcast %struct.tagDISPPARAMS* %param to i8*
      call void @llvm.memset.p0i8.i32(i8* %1, i8 0, i32 16, i32 4, i1 false)
      %2 = call x86_stdcallcc i32 @CoInitialize(i8* null) nounwind
      %3 = call x86_stdcallcc i32 @CLSIDFromProgID(i16* getelementptr inbounds ([18 x i16]* @.str1, i32 0, i32 0), %struct._GUID* %clsid) nounwind
      %4 = bitcast %struct.IDispatch** %pShell to i8**
      %5 = call x86_stdcallcc i32 @CoCreateInstance(%struct._GUID* %clsid, %struct.IUnknown* null, i32 1, %struct._GUID* @IID_IDispatch, i8** %4) nounwind
      %6 = load %struct.IDispatch** %pShell, align 4, !tbaa !0
      %7 = getelementptr inbounds %struct.IDispatch* %6, i32 0, i32 0
      %8 = load %struct.IDispatchVtbl** %7, align 4, !tbaa !0
      %9 = getelementptr inbounds %struct.IDispatchVtbl* %8, i32 0, i32 5
      %10 = load i32 (%struct.IDispatch*, %struct._GUID*, i16**, i32, i32, i32*)** %9, align 4, !tbaa !0
      %11 = call x86_stdcallcc i32 @GetUserDefaultLCID() nounwind
      %12 = call x86_stdcallcc i32 %10(%struct.IDispatch* %6, %struct._GUID* @GUID_NULL, i16** %ptName, i32 1, i32 %11, i32* %dispid) nounwind
      %13 = getelementptr inbounds [4 x %struct.tagVARIANT]* %varg, i32 0, i32 0
      call x86_stdcallcc void @VariantInit(%struct.tagVARIANT* %13) nounwind
      %14 = getelementptr inbounds [4 x %struct.tagVARIANT]* %varg, i32 0, i32 0, i32 0, i32 0, i32 0
      store i16 3, i16* %14, align 8, !tbaa !3
      %15 = getelementptr inbounds [4 x %struct.tagVARIANT]* %varg, i32 0, i32 0, i32 0, i32 0, i32 4
      %16 = bitcast %union.anon.2* %15 to i32*
      store i32 36, i32* %16, align 8, !tbaa !4
      %17 = getelementptr inbounds [4 x %struct.tagVARIANT]* %varg, i32 0, i32 1
      call x86_stdcallcc void @VariantInit(%struct.tagVARIANT* %17) nounwind
      %18 = getelementptr inbounds %struct.tagVARIANT* %17, i32 0, i32 0, i32 0, i32 0
      store i16 3, i16* %18, align 8, !tbaa !3
      %19 = getelementptr inbounds [4 x %struct.tagVARIANT]* %varg, i32 0, i32 1, i32 0, i32 0, i32 4
      %20 = bitcast %union.anon.2* %19 to i32*
      store i32 0, i32* %20, align 8, !tbaa !4
      %21 = getelementptr inbounds [4 x %struct.tagVARIANT]* %varg, i32 0, i32 2
      call x86_stdcallcc void @VariantInit(%struct.tagVARIANT* %21) nounwind
      %22 = getelementptr inbounds %struct.tagVARIANT* %21, i32 0, i32 0, i32 0, i32 0
      store i16 8, i16* %22, align 8, !tbaa !3
      %23 = call x86_stdcallcc i16* @SysAllocString(i16* getelementptr inbounds ([18 x i16]* @.str2, i32 0, i32 0)) nounwind
      %24 = getelementptr inbounds [4 x %struct.tagVARIANT]* %varg, i32 0, i32 2, i32 0, i32 0, i32 4
      %25 = bitcast %union.anon.2* %24 to i16**
      store i16* %23, i16** %25, align 8, !tbaa !0
      %26 = getelementptr inbounds [4 x %struct.tagVARIANT]* %varg, i32 0, i32 3
      call x86_stdcallcc void @VariantInit(%struct.tagVARIANT* %26) nounwind
      %27 = getelementptr inbounds %struct.tagVARIANT* %26, i32 0, i32 0, i32 0, i32 0
      store i16 3, i16* %27, align 8, !tbaa !3
      %28 = getelementptr inbounds [4 x %struct.tagVARIANT]* %varg, i32 0, i32 3, i32 0, i32 0, i32 4
      %29 = bitcast %union.anon.2* %28 to i32*
      store i32 0, i32* %29, align 8, !tbaa !4
      %30 = getelementptr inbounds %struct.tagDISPPARAMS* %param, i32 0, i32 2
      store i32 4, i32* %30, align 4, !tbaa !5
      %31 = getelementptr inbounds %struct.tagDISPPARAMS* %param, i32 0, i32 0
      store %struct.tagVARIANT* %13, %struct.tagVARIANT** %31, align 4, !tbaa !0
      %32 = load %struct.IDispatch** %pShell, align 4, !tbaa !0
      %33 = getelementptr inbounds %struct.IDispatch* %32, i32 0, i32 0
      %34 = load %struct.IDispatchVtbl** %33, align 4, !tbaa !0
      %35 = getelementptr inbounds %struct.IDispatchVtbl* %34, i32 0, i32 6
      %36 = load i32 (%struct.IDispatch*, i32, %struct._GUID*, i32, i16, %struct.tagDISPPARAMS*, %struct.tagVARIANT*, %struct.tagEXCEPINFO*, i32*)** %35, align 4, !tbaa !0
      %37 = load i32* %dispid, align 4, !tbaa !4
      %38 = call x86_stdcallcc i32 @GetUserDefaultLCID() nounwind
      %39 = call x86_stdcallcc i32 %36(%struct.IDispatch* %32, i32 %37, %struct._GUID* @GUID_NULL, i32 %38, i16 zeroext 1, %struct.tagDISPPARAMS* %param, %struct.tagVARIANT* %vResult, %struct.tagEXCEPINFO* null, i32* null) nounwind
      call x86_stdcallcc void @VariantInit(%struct.tagVARIANT* %13) nounwind
      call x86_stdcallcc void @VariantInit(%struct.tagVARIANT* %17) nounwind
      call x86_stdcallcc void @VariantInit(%struct.tagVARIANT* %21) nounwind
      call x86_stdcallcc void @VariantInit(%struct.tagVARIANT* %26) nounwind
      %40 = getelementptr inbounds %struct.tagVARIANT* %vResult, i32 0, i32 0, i32 0, i32 4
      %41 = bitcast %union.anon.2* %40 to %struct.IDispatch**
      %42 = load %struct.IDispatch** %41, align 8, !tbaa !0
      %43 = icmp eq %struct.IDispatch* %42, null
      br i1 %43, label %50, label %44
     
    ; <label>:44                                      ; preds = %0
      %45 = getelementptr inbounds %struct.IDispatch* %42, i32 0, i32 0
      %46 = load %struct.IDispatchVtbl** %45, align 4, !tbaa !0
      %47 = getelementptr inbounds %struct.IDispatchVtbl* %46, i32 0, i32 2
      %48 = load i32 (%struct.IDispatch*)** %47, align 4, !tbaa !0
      %49 = call x86_stdcallcc i32 %48(%struct.IDispatch* %42) nounwind
      br label %50
     
    ; <label>:50                                      ; preds = %0, %44
      %51 = load %struct.IDispatch** %pShell, align 4, !tbaa !0
      %52 = getelementptr inbounds %struct.IDispatch* %51, i32 0, i32 0
      %53 = load %struct.IDispatchVtbl** %52, align 4, !tbaa !0
      %54 = getelementptr inbounds %struct.IDispatchVtbl* %53, i32 0, i32 2
      %55 = load i32 (%struct.IDispatch*)** %54, align 4, !tbaa !0
      %56 = call x86_stdcallcc i32 %55(%struct.IDispatch* %51) nounwind
      call x86_stdcallcc void @CoUninitialize() nounwind
      ret i32 0
    }
     
    declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) nounwind
     
    declare x86_stdcallcc i32 @CoInitialize(i8*)
     
    declare x86_stdcallcc i32 @CLSIDFromProgID(i16*, %struct._GUID*)
     
    declare x86_stdcallcc i32 @CoCreateInstance(%struct._GUID*, %struct.IUnknown*, i32, %struct._GUID*, i8**)
     
    declare x86_stdcallcc i32 @GetUserDefaultLCID()
     
    declare x86_stdcallcc void @VariantInit(%struct.tagVARIANT*)
     
    declare x86_stdcallcc i16* @SysAllocString(i16*)
     
    declare x86_stdcallcc void @CoUninitialize()
     
    !0 = metadata !{metadata !"any pointer", metadata !1}
    !1 = metadata !{metadata !"omnipotent char", metadata !2}
    !2 = metadata !{metadata !"Simple C/C++ TBAA"}
    !3 = metadata !{metadata !"short", metadata !1}
    !4 = metadata !{metadata !"long", metadata !1}
    !5 = metadata !{metadata !"int", metadata !1}

    上記コードは以下のC言語のソースを clang でアセンブリコード出力(clang -S -O4 hello.c)したものに相当する。

    #include <ole2.h>
     
    int main( int argc, char* argv[] )
    {
        CLSID clsid;
        IDispatch* pShell;
        IDispatch* pFolder;
        DISPID dispid;
        OLECHAR* ptName = L"BrowseForFolder";
        DISPPARAMS param = { NULL, NULL, 0, 0 };
        VARIANT varg[4];
        VARIANT vResult;
     
        CoInitialize( NULL );
     
        CLSIDFromProgID(L"Shell.Application", &clsid );
        CoCreateInstance( &clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IDispatch, (void**)&pShell);
        pShell->lpVtbl->GetIDsOfNames( (void*)pShell, &IID_NULL, &ptName, 1, GetUserDefaultLCID(), &dispid );
     
        VariantInit( &varg[0] );
        varg[0].vt = VT_I4;
        varg[0].lVal = 36L;  /* ssfWINDOWS */
     
        VariantInit( &varg[1] );
        varg[1].vt = VT_I4;
        varg[1].lVal = 0L;
     
        VariantInit( &varg[2] );
        varg[2].vt = VT_BSTR;
        varg[2].bstrVal = SysAllocString(L"Hello, COM World!"); 
     
        VariantInit( &varg[3] );
        varg[3].vt = VT_I4;
        varg[3].lVal = 0L;
     
        param.cArgs = 4;
        param.rgvarg = varg;
     
        pShell->lpVtbl->Invoke( (void*)pShell, dispid, &IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD, &param, &vResult, NULL, NULL );
     
        VariantInit( &varg[0] );
        VariantInit( &varg[1] );
        VariantInit( &varg[2] );
        VariantInit( &varg[3] );
     
        pFolder = V_DISPATCH( &vResult );
        if ( pFolder != NULL )
        {
            pFolder->lpVtbl->Release( (void*)pFolder );
        }
        pShell->lpVtbl->Release( (void*)pShell );
     
        CoUninitialize();
     
        return 0;
    }

    コンパイル&リンク方法

    C:¥> clang -o hello hello.ll -l ole32  -l oleaut32 -l uuid

    実行結果

    +----------------------------------------+
    |Browse For Folder                    [X]|
    +----------------------------------------+
    | Hello, COM Wolrd!                      |
    |                                        |
    | +------------------------------------+ |
    | |[Windows]                           | |
    | | +[addins]                          | |
    | | +[AppCompat]                       | |
    | | +[AppPatch]                        | |
    | | +[assembly]                        | |
    | |     :                              | |
    | |     :                              | |
    | |     :                              | |
    | +------------------------------------+ |
    | [Make New Folder]    [  OK  ] [Cancel] |
    +----------------------------------------+