2011-08-22 7 views
7

Tôi cần một số lời khuyên về các đối số "chuyển tiếp" cho một callee (trong LLVM-IR).Chuyển tiếp đối số trong LLVM

Giả sử tôi có chức năng F được gọi ở đầu tất cả các chức năng khác trong mô-đun. Từ F Tôi cần truy cập (đọc) các đối số được truyền cho người gọi ngay lập tức. Ngay bây giờ để làm điều này, tôi đánh dấu tất cả các đối số trong người gọi bên trong một cấu trúc và chuyển con trỏ i8* đến cấu trúc đến F, cùng với số nhận dạng cho biết người gọi F đang được gọi từ đó. F sau đó có một công tắc khổng lồ chuyển nhánh tới mã unboxing thích hợp. Điều này phải được thực hiện bởi vì các hàm trong mô-đun có các chữ ký khác nhau (khác nhau đối số/giá trị trả về và kiểu; thậm chí khác nhau quy ước gọi), nhưng nó rõ ràng là tối ưu (cả từ một hiệu suất và kích thước mã điểm-of-view) vì tôi cần phải phân bổ cấu trúc trên ngăn xếp, sao chép các đối số bên trong của nó, chuyển một con trỏ bổ sung đến F và sau đó thực hiện unboxing. Tôi đã tự hỏi nếu có một cách tốt hơn để làm điều này, tức là một cách để truy cập từ một hàm khung ngăn xếp của người gọi ngay lập tức của nó (biết, nhờ định danh, mà người gọi hàm được gọi từ) hoặc , nói chung hơn, giá trị tùy ý được xác định trong người gọi ngay lập tức. Bất kỳ đề xuất?

lưu ý: toàn bộ điểm của những gì tôi đang làm là có một hàm F thực hiện tất cả điều này; tách/inlining/chuyên/templating F không phải là một lựa chọn.


để làm rõ, giả sử chúng ta có các chức năng sau FuncAFuncB (lưu ý: những gì sau chỉ là giả C-mã, luôn nhớ là chúng tôi đang nói về LLVM-IR!)

Type1 FuncA(Type2 ArgA1) { 
    F(); 
    // ... 
} 

Type3 FuncB(Type4 ArgB1, Type5 ArgB2, Type6 ArgB3) { 
    F(); 
    // ... 
} 

những gì tôi cần là một cách hiệu quả cho hàm F phải làm như sau:

void F() { 
    switch (caller) { 
    case FuncA: 
     // do something with ArgA1 
     break; 
    case FuncB: 
     // do something with ArgB1, ArgB2, ArgB3 
     break; 
    } 
} 

như tôi đã giải thích ở phần đầu tiên, ngay bây giờ tôi 01.trông như thế này:

struct Args_FuncA { Type2 ArgA1 }; 
struct Args_FuncB { Type4 ArgB1, Type5 ArgB2, Type6 ArgB3 }; 

void F(int callerID, void *args) { 
    switch (callerID) { 
    case ID_FuncA: 
     Args_FuncA *ArgsFuncA = (Args_FuncA*)args; 
     Type2 ArgA1 = ArgsFuncA->ArgA1; 
     // do something with ArgA1 
     break; 
    case ID_FuncB: 
     Args_FuncB *ArgsFuncB = (Args_FuncB*)args; 
     Type4 ArgB1 = ArgsFuncB->ArgB1; 
     Type5 ArgB2 = ArgsFuncB->ArgB2; 
     Type6 ArgB3 = ArgsFuncB->ArgB3; 
     // do something with ArgB1, ArgB2, ArgB3 
     break; 
    } 
} 

và hai chức năng trở thành:

Type1 FuncA(Type2 ArgA1) { 
    Args_FuncA args = { ArgA1 }; 
    F(ID_FuncA, (void*)&args); 
    // ... 
} 

Type3 FuncB(Type4 ArgB1, Type5 ArgB2, Type6 ArgB3) { 
    Args_FuncB args = { ArgB1, ArgB2, ArgB3 }; 
    F(ID_FuncB, (void*)&args); 
    // ... 
} 

Trả lời

1

IMHO bạn đã làm điều đó đúng. Trong khi có các giải pháp trong lắp ráp machinecode, tôi sợ có thể không có giải pháp trong hội đồng LLVM, vì nó là "cấp cao hơn". Nếu bạn muốn chạy một hàm trên đầu một số chức năng bạn có nghĩ về việc kiểm tra

  • nguồn debugger (như gdb)
  • Binary Instrumentation với Valgrind

Tôi biết nó không phải đạo câu trả lời, nhưng tôi hy vọng nó có thể hữu ích trong một số cách;).

+1

dtrace đã làm giống như mô tả CAFxX. – osgx

1

Không chắc chắn nếu điều này giúp, nhưng tôi đã có một vấn đề tương tự và có xung quanh những hạn chế của phân tích tbaa LLVM bằng cách sử dụng một vectơ llvm để lưu trữ các giá trị trung gian. Các giá trị tối ưu hóa LLVM sau đó có thể tối ưu hóa tải/cửa hàng vector vào thanh ghi vô hướng.

Có một vài cảnh báo khi tôi nhớ lại. Hãy cho tôi biết nếu bạn khám phá tuyến đường này và tôi có thể đào một số mã.