从LLVM IR访问结构成员和结构数组

Pau*_*cas 6 c++ jit llvm

如果我有一个声明a的C++程序struct,请说:

struct S {
    short s;
    union U {
        bool b;
        void *v;
    };
    U u;
};
Run Code Online (Sandbox Code Playgroud)

我通过LLVM C++ API生成一些LLVM IR来镜像C++声明:

vector<Type*> members;
members.push_back( IntegerType::get( ctx, sizeof( short ) * 8 ) );
// since LLVM doesn't support unions, just use an ArrayType that's the same size
members.push_back( ArrayType::get( IntegerType::get( ctx, 8 ), sizeof( S::U ) ) );

StructType *const llvm_S = StructType::create( ctx, "S" );
llvm_S->setBody( members );
Run Code Online (Sandbox Code Playgroud)

如何确保sizeof(S)C++代码与StructTypeLLVM IR代码中的大小相同?对于各个成员的抵消也是如此,即u.b.

我也有一个S用C++分配的数组:

S *s_array = new S[10];
Run Code Online (Sandbox Code Playgroud)

然后我传递s_array给LLVM IR代码,我在其中访问数组的各个元素.为了使其工作,sizeof(S)在C++和LLVM IR中都必须相同,所以这样:

%elt = getelementptr %S* %ptr_to_start, i64 1
Run Code Online (Sandbox Code Playgroud)

s_array[1]正确访问.

当我编译并运行下面的程序时,它会输出:

sizeof(S) = 16
allocSize(S) = 10
Run Code Online (Sandbox Code Playgroud)

问题是LLVM在S::s和之间缺少6个字节的填充S::u.C++编译器union在8字节对齐的边界上启动,而LLVM则不启动.

我在玩弄DataLayout.对于我的机器[Mac OS X 10.9.5,g ++ Apple LLVM 6.0(clang-600.0.57)(基于LLVM 3.5svn)],如果我打印数据布局字符串,我得到:

e-m:o-i64:64-f80:128-n8:16:32:64-S128
Run Code Online (Sandbox Code Playgroud)

如果我强制设置数据布局:

e-m:o-i64:64-f80:128-n8:16:32:64-S128-a:64
Run Code Online (Sandbox Code Playgroud)

添加的a:64意思是聚合类型的对象在64位边界上对齐,然后我得到相同的大小.那么为什么默认数据布局不正确呢?


完整的工作程序如下

// LLVM
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Type.h>
#include <llvm/Support/TargetSelect.h>

// standard
#include <iostream>
#include <memory>
#include <string>

using namespace std;
using namespace llvm;

struct S {
    short s;
    union U {
        bool b;
        void *v;
    };
    U u;
};

ExecutionEngine* createEngine( Module *module ) {
    InitializeNativeTarget();
    InitializeNativeTargetAsmPrinter();

    unique_ptr<Module> u( module );
    EngineBuilder eb( move( u ) );
    string errStr;
    eb.setErrorStr( &errStr );
    eb.setEngineKind( EngineKind::JIT );
    ExecutionEngine *const exec = eb.create();
    if ( !exec ) {
        cerr << "Could not create ExecutionEngine: " << errStr << endl;
        exit( 1 );
    }
    return exec;
}

int main() {
    LLVMContext ctx;

    vector<Type*> members;
    members.push_back( IntegerType::get( ctx, sizeof( short ) * 8 ) );
    members.push_back( ArrayType::get( IntegerType::get( ctx, 8 ), sizeof( S::U ) ) );

    StructType *const llvm_S = StructType::create( ctx, "S" );
    llvm_S->setBody( members );

    Module *const module = new Module( "size_test", ctx );
    ExecutionEngine *const exec = createEngine( module );
    DataLayout const *const layout = exec->getDataLayout();
    module->setDataLayout( layout );

    cout << "sizeof(S) = " << sizeof( S ) << endl;
    cout << "allocSize(S) = " << layout->getTypeAllocSize( llvm_S ) << endl;

    delete exec;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*son 5

由于原始答案是对“预编辑”问题的正确答案,因此我正在为新问题写一个全新的答案(我的猜测是结构实际上并不相同,这是相当不错的)。

这个问题是不是DataLayout因为这样,[但你需要DataLayout解决的问题,所以你需要更新的代码开始让LLVM-IR之前创建的模块],但要结合一个事实union有对齐限制struct较小的对齐限制:

struct S {
    short s;        // Alignment = 2 
    union U {    
        bool b;     // Alignment = 1
        void *v;    // Alignment = 4 or 8
    };
    U u;            // = Alignment = 4 or 8
};
Run Code Online (Sandbox Code Playgroud)

现在在您的LLVM代码中:

members.push_back( IntegerType::get( ctx, sizeof( short ) * 8 ) );
members.push_back( ArrayType::get( IntegerType::get( ctx, 8 ), sizeof( S::U ) ) );
Run Code Online (Sandbox Code Playgroud)

结构中的第二个元素是char dummy[sizeof(S::U)],其对齐要求为1。因此,当然,struct与具有更严格对齐标准的C ++编译器相比,LLVM的对齐方式有所不同。

在此特定情况下,使用i8 *(aka void *)代替of数组i8将达到目的[显然,bitcast在访问b] 的值时,有必要根据需要将Related 转换为其他类型。

要解决此问题,您需要以一种完全通用的方式生成一个,struct其中包含,其中具有最大对齐要求的元素union,然后用足够的char元素填充它以弥补最大的尺寸。

我现在要吃点东西,但是我将返回一些可以正确解决问题的代码,但是它比我最初想象的要复杂一些。

这是main上面发布的修改为使用指针而不是char数组的方法:

int main() {
    LLVMContext ctx;

    vector<Type*> members;
    members.push_back( IntegerType::get( ctx, sizeof( short ) * 8 ) );
    members.push_back( PointerType::getUnqual( IntegerType::get( ctx, 8 ) ) );

    StructType *const llvm_S = StructType::create( ctx, "S" );
    llvm_S->setBody( members );

    Module *const module = new Module( "size_test", ctx );
    ExecutionEngine *const exec = createEngine( module );
    DataLayout const *const layout = exec->getDataLayout();
    module->setDataLayout( *layout );

    cout << "sizeof(S) = " << sizeof( S ) << endl;
    cout << "allocSize(S) = " << layout->getTypeAllocSize( llvm_S ) << endl;

    delete exec;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

setDataLayout在您的LLVM版本和我使用的LLVM版本之间也发生了一些微小的变化,以涵盖这一事实。

最后是允许使用任何类型的通用版本:

Type* MakeUnionType( Module* module, LLVMContext& ctx, vector<Type*> um )
{
    const DataLayout dl( module );
    size_t maxSize = 0;
    size_t maxAlign = 0;
    Type*  maxAlignTy = 0;

    for( auto m : um )
    {
        size_t sz = dl.getTypeAllocSize( m );
        size_t al = dl.getPrefTypeAlignment( m );
        if( sz > maxSize ) 
            maxSize = sz;
        if( al > maxAlign) 
        {
            maxAlign = al;
            maxAlignTy = m;
        }
    }
    vector<Type*> sv = { maxAlignTy };
    size_t mas = dl.getTypeAllocSize( maxAlignTy );
    if( mas < maxSize )
    {
        size_t n = maxSize - mas;
        sv.push_back(ArrayType::get( IntegerType::get( ctx, 8 ), n ) );
    }
    StructType* u = StructType::create( ctx, "U" );
    u->setBody( sv );
    return u;
}

int main() {
    LLVMContext ctx;

    Module *const module = new Module( "size_test", ctx );
    ExecutionEngine *const exec = createEngine( module );
    DataLayout const *const layout = exec->getDataLayout();
    module->setDataLayout( *layout );

    vector<Type*> members;
    members.push_back( IntegerType::get( ctx, sizeof( short ) * 8 ) );
    vector<Type*> unionMembers = { PointerType::getUnqual( IntegerType::get( ctx, 8 ) ), 
                   IntegerType::get( ctx, 1 )  };
    members.push_back( MakeUnionType( module, ctx, unionMembers ) );

    StructType *const llvm_S = StructType::create( ctx, "S" );
    llvm_S->setBody( members );

    cout << "sizeof(S) = " << sizeof( S ) << endl;
    cout << "allocSize(S) = " << layout->getTypeAllocSize( llvm_S ) << endl;

    delete exec;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,在这两种情况下,都需要一个bitcast操作来转换-的地址,b在第二种情况下,还需要位转换来将转换structvoid *,但是假设您实际上需要通用union支持,那是您必须要做的无论如何。

union可以在这里找到完整的代码来生成类型,这是我的Pascal编译器的variant[这是Pascal制作union] 的方法:

https://github.com/Leporacanthicus/lacsap/blob/master/types.cpp#L525 和代码生成(包括位播):https : //github.com/Leporacanthicus/lacsap/blob/master/expr.cpp#L520