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 FuncA
và FuncB
(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);
// ...
}
dtrace đã làm giống như mô tả CAFxX. – osgx