Delphi XE字节数组索引

时间:2016-07-22 22:02:14

标签: arrays delphi indexing bytebuffer compiler-bug

我使用像这样的简单循环缓冲区

var
  Values: array [byte] of single;
  ptr: byte;

在此测试示例中

for ptr:=0 to 10 do Values[Byte(ptr-5)]:=1;

我希望设置为1前5个值和最后5个值,但XE4 compiller产生错误的代码,它使用32位指针数学来计算数组索引:

for ptr:=0 to 10 do Values[Byte(ptr-5)]:=1;
005B94BB C645FB00         mov byte ptr [ebp-$05],$00
005B94BF 33C0             xor eax,eax
005B94C1 8A45FB           mov al,[ebp-$05]
005B94C4 C78485E0FBFFFF0000803F mov [ebp+eax*4-$0420],$3f800000
005B94CF FE45FB           inc byte ptr [ebp-$05]
005B94D2 807DFB0B         cmp byte ptr [ebp-$05],$0b
005B94D6 75E7             jnz $005b94bf

这是我的错误代码和操作字节索引的正确方法吗?

3 个答案:

答案 0 :(得分:4)

问题是:

  

预期在Byte()演员表中?

让我们将反汇编与开启/关闭溢出检查进行比较。

{$Q+}
Project71.dpr.21: for ptr:= 0 to 10 do Values[Byte(ptr-5)]:= 1;
0041D568 33DB             xor ebx,ebx
0041D56A 0FB6C3           movzx eax,bl
0041D56D 83E805           sub eax,$05
0041D570 7105             jno $0041d577
0041D572 E82D8DFEFF       call @IntOver
0041D577 0FB6C0           movzx eax,al
0041D57A C704870000803F   mov [edi+eax*4],$3f800000
0041D581 43               inc ebx
0041D582 80FB0B           cmp bl,$0b
0041D585 75E3             jnz $0041d56a

{$Q-}
Project71.dpr.21: for ptr:= 0 to 10 do Values[Byte(ptr-5)]:= 1;
0041D566 B30B             mov bl,$0b
0041D568 B808584200       mov eax,$00425808
0041D56D C7000000803F     mov [eax],$3f800000
0041D573 83C004           add eax,$04
0041D576 FECB             dec bl
0041D578 75F3             jnz $0041d56d

使用{$Q+}包装工作,而使用{$Q-}包装不起作用,并且在设置{$R+}时,编译器不会为错误的数组索引生成范围错误。

所以,对我来说结论是:由于range check on没有为数组索引超出边界生成运行时错误,预期包装

当溢出检查开启时,完成包装的事实进一步证明了这一点。

这应该被报告为编译器中的错误。

完成: https://quality.embarcadero.com/browse/RSP-15527“数组转换失败”

注意:@Rudy在答案中提供了一种解决方法。

<强>附录:

以下代码:

for ptr:= 0 to 10 do WriteLn(Byte(ptr-5));

产生

251
252
253
254
255
0
1
2
3
4
5

用于范围/溢出检查的所有组合。

同样Values[Byte(-1)] := 1;为所有编译器选项的值[255]分配1。

Value Typecasts的文档说:

  

通过转换括号中的表达式获得结果值。如果指定类型的大小与表达式的大小不同,则可能涉及截断或扩展。表达式的符号始终保留。

答案 1 :(得分:3)

我的代码是用Delphi 10.1 Berlin编写的,但结果似乎是一样的。

让我们稍微扩展您的小代码:

node

这在CPU视图中提供以下代码:

procedure Test;
var
  Values: array[Byte] of Single;
  Ptr: byte;
begin
  Values[0] := 1.0;
  for Ptr := 0 to 10 do
    Values[Byte(Ptr - 5)] := 1.0;
end;

正如我们所看到的,数组的第一个元素位于Project80.dpr.15: Values[0] := 1.0; 0041A1DD C785FCFBFFFF0000803F mov [ebp-$00000404],$3f800000 Project80.dpr.16: for Ptr := 0 to 10 do 0041A1E7 C645FF00 mov byte ptr [ebp-$01],$00 Project80.dpr.17: Values[Byte(Ptr-5)] := 1.0; 0041A1EB 33C0 xor eax,eax 0041A1ED 8A45FF mov al,[ebp-$01] 0041A1F0 C78485E8FBFFFF0000803F mov [ebp+eax*4-$0418],$3f800000 0041A1FB FE45FF inc byte ptr [ebp-$01] Project80.dpr.16: for Ptr := 0 to 10 do 0041A1FE 807DFF0B cmp byte ptr [ebp-$01],$0b 0041A202 75E7 jnz $0041a1eb ,因此[ebp-$00000404]确实低于数组(对于值0..4)。

这对我来说似乎是一个错误 ,因为对于[ebp+eax*4-$0418]Ptr = 0应该回到Byte(Ptr - 5)。生成的代码应该是:

$FB

好找!

但有一种解决方法:

    mov byte ptr [ebp-$01],$00
    xor eax,eax
@loop:
    mov al,[ebp-$01]
    sub al,5                        // Byte(Ptr - 5)
    mov [ebp+4*eax-$0404],$3f800000 // al = $FB, $FC, $FD, $FE, $FF, 00, etc..
    inc byte ptr [ebp-$01]
    cmp byte ptr [ebp-$01],$0b
    jnz @loop

这会产生:

    Values[Byte(Ptr - 5) + 0] := 1.0;

这很好用,虽然Project80.dpr.19: Values[Byte(Ptr - 5) + 0] := 1.0; 0040F16B 8A45FF mov al,[ebp-$01] 0040F16E 2C05 sub al,$05 0040F170 25FF000000 and eax,$000000ff 0040F175 C78485FCFBFFFF0000803F mov [ebp+eax*4-$0404],$3f800000 对我来说似乎没用。

FWIW,我还查看了优化生成的代码。在XE和柏林,错误都存在,并且解决方法也适用。

答案 2 :(得分:1)

听起来像是编译器的意外行为。但是我从不认为使用byte()的转换整数总是围绕$ff进行四舍五入。在大多数情况下,它确实例如如果你在变量之间分配值,但有些情况下它并没有 - 正如你所发现的那样。所以我从未在数组索引计算中使用过这个byte()表达式。

我一直认为使用byte变量是不值得的,你应该使用普通的integer(或NativeInt),以便它与CPU寄存器匹配,然后don&假设任何复杂的舍入。

在所有情况下,我宁愿将255舍入显式化,如下:

procedure test;
var
  Values: array [byte] of single;
  ptr: integer;
begin
  for ptr:=0 to 10 do Values[(ptr-5) and high(Values)]:=1;
end;

如您所见,我做了一些修改:

  • for循环索引定义为整数,以使用CPU寄存器;
  • 使用and操作进行快速二进制舍入(写(ptr-5) mod 256 更慢);
  • 使用high(Values)代替固定$ff常量,表示此舍入的来源。

然后快速优化生成的代码:

TestAll.dpr.114: begin
0064810C 81C400FCFFFF     add esp,$fffffc00
TestAll.dpr.115: for ptr:=0 to 10 do Values[(ptr-5) and high(Values)]:=1;
00648112 33C0             xor eax,eax
00648114 8BD0             mov edx,eax
00648116 83EA05           sub edx,$05
00648119 81E2FF000000     and edx,$000000ff
0064811F C704940000803F   mov [esp+edx*4],$3f800000
00648126 40               inc eax
00648127 83F80B           cmp eax,$0b
0064812A 75E8             jnz -$18
TestAll.dpr.116: end;
0064812C 81C400040000     add esp,$00000400
00648132 C3               ret