Skip to content

Instantly share code, notes, and snippets.

@aguinet
Last active August 19, 2018 17:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aguinet/5d359476d77ff69a7daafa51ad005ea8 to your computer and use it in GitHub Desktop.
Save aguinet/5d359476d77ff69a7daafa51ad005ea8 to your computer and use it in GitHub Desktop.
import struct
import pydffi
import llvmlite.ir as ll
FFI = pydffi.FFI()
CU = FFI.cdef('''
#include <stdbool.h>
typedef struct {
bool b;
int i;
short s;
} A;
typedef struct {
bool b;
A a;
} B;
''')
A = CU.types.A
B = CU.types.B
I8Ty = ll.IntType(8)
I8PtrTy = ll.PointerType(I8Ty)
I32Ty = ll.IntType(32)
M = ll.Module()
# From http://code.activestate.com/recipes/577514-chek-if-a-number-is-a-power-of-two/
def isPower2(num):
return num != 0 and ((num & (num - 1)) == 0)
def llvm_alloc_ty(IRB, Ty):
Ret = IRB.alloca(ll.ArrayType(I8Ty, Ty.size))
return Ret
def get_memcpy(IRB):
return IRB.module.declare_intrinsic('llvm.memcpy', [I8PtrTy, I8PtrTy, I32Ty])
def llvm_set_field(IRB, Ptr, Field, Obj):
Ty = Field.type
Ptr = IRB.bitcast(Ptr, I8PtrTy)
Ptr = IRB.gep(Ptr, [ll.Constant(I32Ty, Field.offset)])
Size = pydffi.sizeof(Obj)
Data = pydffi.view_as_bytes(Obj)
if len(Data) != Size:
raise ValueError("sizeof(Obj) should be equals to sizeof(Field.type)")
SizesFormat = {1: "B", 2: "H", 4: "I", 8: "Q"}
Format = SizesFormat.get(Size, None)
if not Format is None:
# If size is a multiple of two and < 64-bit, use a scalar to store the value
v = struct.unpack(Format, Data)[0]
DataTy = ll.IntType(Size*8)
Ptr = IRB.bitcast(Ptr, ll.PointerType(DataTy))
IRB.store(ll.Constant(DataTy, v), Ptr)
else:
# If this is not the case, store the value in a GV and copy it!
DataTy = ll.ArrayType(I8Ty, Size)
GVData = ll.GlobalVariable(IRB.module, DataTy, "dffi_cst")
GVData.global_constant = True
GVData.initializer = ll.Constant(DataTy, [int(v) for v in Data])
memcpy = get_memcpy(IRB)
IRB.call(memcpy, [Ptr, IRB.bitcast(GVData, I8PtrTy), ll.Constant(I32Ty, Size), ll.Constant(I32Ty, Ty.align), ll.Constant(ll.IntType(1),0)])
def llvm_get_field(IRB, Ptr, OutPtr, Field):
Ty = Field.type
Ptr = IRB.bitcast(Ptr, I8PtrTy)
Ptr = IRB.gep(Ptr, [ll.Constant(I32Ty, Field.offset)])
Size = Ty.size
if Size <= 8 and isPower2(Size):
DataTy = ll.IntType(Size*8)
DataPtrTy = ll.PointerType(DataTy)
Ptr = IRB.bitcast(Ptr, DataPtrTy)
IRB.store(IRB.load(Ptr), IRB.bitcast(OutPtr, DataPtrTy))
else:
DataTy = ll.ArrayType(I8Ty, Size)
memcpy = get_memcpy(IRB)
IRB.call(memcpy, [IRB.bitcast(OutPtr, I8PtrTy), Ptr, ll.Constant(I32Ty, Size), ll.Constant(I32Ty, Ty.align), ll.Constant(ll.IntType(1),0)])
# Generate a function that sets "A.i" to 10
FTy = ll.FunctionType(ll.VoidType(), [I8PtrTy])
F = ll.Function(M, FTy, name='A_set_i_10')
BB = F.append_basic_block()
IRB = ll.IRBuilder()
IRB.position_at_end(BB)
llvm_set_field(IRB, F.args[0], A.i, FFI.Int(4))
IRB.ret_void()
# Generate a function that gather "A.i"
FTy = ll.FunctionType(I32Ty, [I8PtrTy])
F = ll.Function(M, FTy, name='A_get_i')
BB = F.append_basic_block()
IRB = ll.IRBuilder()
IRB.position_at_end(BB)
Ret = IRB.alloca(I32Ty)
llvm_get_field(IRB, F.args[0], Ret, A.i)
IRB.ret(IRB.load(Ret))
Av = A(b=1,i=0x41414141,s=0x4242)
# Generate a function that sets "B.a" to Av
FTy = ll.FunctionType(ll.VoidType(), [I8PtrTy])
F = ll.Function(M, FTy, name='B_set_a')
BB = F.append_basic_block()
IRB = ll.IRBuilder()
IRB.position_at_end(BB)
llvm_set_field(IRB, F.args[0], B.a, Av)
IRB.ret_void()
# Generate a function that gather "B.a"
FTy = ll.FunctionType(ll.VoidType(), [I8PtrTy, I8PtrTy])
F = ll.Function(M, FTy, name='B_get_a')
BB = F.append_basic_block()
IRB = ll.IRBuilder()
IRB.position_at_end(BB)
llvm_get_field(IRB, F.args[0], F.args[1], B.a)
IRB.ret_void()
print(M)
@aguinet
Copy link
Author

aguinet commented Aug 19, 2018

Output:

$ python ./structs_llvmlite.py  |opt-6.0 -S -O
; ModuleID = '<stdin>'
source_filename = "<stdin>"
target triple = "unknown-unknown-unknown"

@dffi_cst = local_unnamed_addr constant [12 x i8] c"\01T\DD\00AAAABB\D3\00"

; Function Attrs: norecurse nounwind
define void @A_set_i_10(i8* nocapture %.1) local_unnamed_addr #0 {
.3:
  %.4 = getelementptr i8, i8* %.1, i64 4
  %.5 = bitcast i8* %.4 to i32*
  store i32 4, i32* %.5, align 4
  ret void
}

; Function Attrs: norecurse nounwind readonly
define i32 @A_get_i(i8* nocapture readonly %.1) local_unnamed_addr #1 {
.3:
  %.5 = getelementptr i8, i8* %.1, i64 4
  %.6 = bitcast i8* %.5 to i32*
  %.7 = load i32, i32* %.6, align 4
  ret i32 %.7
}

; Function Attrs: nounwind
define void @B_set_a(i8* nocapture %.1) local_unnamed_addr #2 {
.3:
  %.4 = getelementptr i8, i8* %.1, i64 4
  tail call void @llvm.memcpy.p0i8.p0i8.i32(i8* %.4, i8* getelementptr inbounds ([12 x i8], [12 x i8]* @dffi_cst, i64 0, i64 0), i32 12, i32 4, i1 false)
  ret void
}

; Function Attrs: argmemonly nounwind
declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i1) #3

; Function Attrs: nounwind
define void @B_get_a(i8* nocapture readonly %.1, i8* nocapture %.2) local_unnamed_addr #2 {
.4:
  %.5 = getelementptr i8, i8* %.1, i64 4
  tail call void @llvm.memcpy.p0i8.p0i8.i32(i8* %.2, i8* %.5, i32 12, i32 4, i1 false)
  ret void
}

attributes #0 = { norecurse nounwind }
attributes #1 = { norecurse nounwind readonly }
attributes #2 = { nounwind }
attributes #3 = { argmemonly nounwind }

@aguinet
Copy link
Author

aguinet commented Aug 19, 2018

This needs the last version of dragonffi on master (aguinet/dragonffi@8afdc94)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment