2009-09-30 22 views
6

Im tạo ra một số IL với ILGenerator đây là mã của tôi:C# ILGenerator nop?

DynamicMethod method = new DynamicMethod("test", null, Type.EmptyTypes); 
ILGenerator gen = method.GetILGenerator(); 
gen.Emit(OpCodes.Ldarg_0); 
gen.Emit(OpCodes.Ldarg_1); 
gen.Emit(OpCodes.Ldc_I4_S, 100); 

này tạo ra IL này:

IL_0000: ldarg.0  
IL_0001: ldarg.1  
IL_0002: ldc.i4.s 100 
IL_0004: nop   
IL_0005: nop   
IL_0006: nop   

(tôi nhận được Mã IL từ một VS Virtulizer tên ILStream)

Từ mã nops ở đâu? có cách nào để loại bỏ chúng không? Tôi đang cố gắng bắt chước một số mã C# và nó không có 3 bước nhảy.

+2

Có lẽ nó đóng gói các hướng dẫn vào một khối có kích thước nhất định và điền phần còn lại với nops? – Joe

+0

Vì NOP không gây hại gì, tại sao bạn muốn loại bỏ chúng? – RichardOD

+0

Nếu chúng không có lý do tại sao có chúng ở đó, mã C# không có bất kỳ ... – Peter

Trả lời

3

tôi giải quyết vấn đề bằng cách đúc các int đến một giá trị:

Code:

private static bool IsBetween(int value, int min, int max) 
{ 
    return (value >= min && value <= max); 
} 

private static void WriteInt(ILGenerator gen, int value) 
{ 
    gen.Emit(OpCodes.Ldarg_1); 
    if (IsBetween(value, sbyte.MinValue, sbyte.MaxValue)) 
    { 
     gen.Emit(OpCodes.Ldc_I4_S, (sbyte)value); 
    } 
    else if (IsBetween(value, byte.MinValue, byte.MaxValue)) 
    { 
     gen.Emit(OpCodes.Ldc_I4_S, (byte)value); 
    } 
    else if (IsBetween(value, short.MinValue, short.MaxValue)) 
    { 
     gen.Emit(OpCodes.Ldc_I4_S, (short)value); 
    } 
    else 
    { 
     gen.Emit(OpCodes.Ldc_I4_S, value); 
    } 
} 
8

Bạn đang đi đúng hướng để thoát khỏi những "nop" s:

Khi bạn cung cấp một đối số bổ sung cho một cuộc gọi Emit, luôn luôn chắc chắn để kiểm tra trên MSDN cho loại đối số thích hợp.

Đối OpCodes.Ldc_I4_S, MSDN khẳng định:

ldc.i4.s là một mã hóa hiệu quả hơn để đẩy các số nguyên từ -128 đến 127 vào> đánh giá stack.

Sau đây Emit phương pháp quá tải có thể sử dụng opcode ldc.i4.s:

ILGenerator.Emit (opcode, byte)

Vì vậy, phần thứ hai của mã của bạn sẽ có kết quả không thể đoán trước (ngoài những kết quả không mong muốn) vào thời gian chạy, vì bạn đang cố gắng tải "int8" trên ngăn xếp nhưng cung cấp giá trị "int32" hoặc "ngắn":

else if (IsBetween(value, short.MinValue, short.MaxValue)) 
{ 
    gen.Emit(OpCodes.Ldc_I4_S, (short)value); 
} 
else 
{ 
    gen.Emit(OpCodes.Ldc_I4_S, value); 
} 

Bạn nên sử dụng Ldc_I4 thay vì Ldc_I4_S nếu bạn muốn tải đúng int32/ngắn (hoặc bất kỳ thứ gì có độ lớn hơn một byte) lên ngăn xếp. Vì vậy, mã của bạn sẽ trông như thế này thay vì ví dụ trên:

else 
{ 
    gen.Emit(OpCodes.Ldc_I4, value); 
} 

Đây là một đoán hoang dã, nhưng của ba nop đã được tạo ra có thể có cái gì để làm với các byte thêm từ int32 bạn

Hy vọng rằng sẽ giúp ...

+2

Có, khi bạn sử dụng quá tải int thay vì quá tải byte, bộ tạo (có vẻ như nó sẽ xuất hiện) phát ra đối số một số nguyên 4 byte khi opcode mong đợi một đối số 1 byte. Bởi vì nó được phát ra trong một chút endian, con số này là một giá trị dương nhỏ hơn 128 và lệnh nop xảy ra là một byte '0x00' duy nhất, nó có tác dụng thêm 3 lệnh nop.Nếu số nguyên xảy ra là 128 thì nó sẽ được nạp 0 vào ngăn xếp và lệnh nop đầu tiên sẽ được thay thế bằng lệnh ngắt. –

+0

Không nên là 'else if (IsBetween (giá trị, sbyte.MinValue, sbyte.MaxValue)) gen.Emit (OpCodes.Ldc_I4_S, (ngắn) giá trị); khác gen.Emit (OpCodes.Ldc_I4, value); ' – aboveyou00

+1

Đúng, tôi thậm chí còn lấy nó hơn nữa và nói nó nên là gen.Emit ((OpCodes.Ldc_I4_S, (byte) value); else ... Tuy nhiên , lưu ý rằng mẫu mã đầu tiên là ví dụ về những gì là sai, không phải những gì là đúng. –

Các vấn đề liên quan