Bug ? in for next loop

General FreeBASIC programming questions.
Post Reply
SARG
Posts: 1755
Joined: May 27, 2005 7:15
Location: FRANCE

Bug ? in for next loop

Post by SARG »

It is a bug with for/next loop using unsigned iterator and negative increment ?

I can't figure why the first two loops are not executed as the two last....

Code: Select all

For i As UByte =3 To 1 Step -1
	Print "UByte",i
Next
For i As Ushort =3 To 1 Step -1
	Print "Ushort",i
Next
For i As UInteger =3 To 1 Step -1
	Print "UInteger",i
Next
For i As ULongInt =3 To 1 Step -1
	Print "ULongInt",i
Next
Sleep
KristopherWindsor
Posts: 2428
Joined: Jul 19, 2006 19:17
Location: Sunnyvale, CA
Contact:

Post by KristopherWindsor »

It seems buggish, but I can guess what it's doing. If it's casting the step value to the iterator type then it won't think the step value is negative.
I think it's just adding the step to the iterator, when it could be doing this to fix the bug:
If stepvalue < 0 then iterator -= abs(stepvalue) else iterator += stepvalue
But the best solution is to just use signed types in this case.

Code: Select all

Screen 12

#macro x(t)
Print #t, cast(t, 3), cast(t, 1), cast(t, -1)
For i As t = cast(t, 3) To cast(t, 1) Step cast(t, -1)
  Print #t, i
Next
? : ?
#endmacro

x(Byte)

x(Ubyte)
x(Ushort)
x(Uinteger)
x(Ulongint)

Sleep
Why does it work for uintegers and larger? Maybe because the ubytes are temporarily converted to uintegers for comparison, where -1 = 255, but the uintegers are processed as uintegers, where adding -1 (=&HFFFFFFFF) will cause an overflow and appear to work correctly.
Richard
Posts: 3096
Joined: Jan 15, 2007 20:44
Location: Australia

Post by Richard »

For best loop speed the iterator needs to be the same type as the counter.
If your counter is unsigned then your iterator should also be unsigned.
You should not be casting -1 to an unsigned type.
SARG
Posts: 1755
Joined: May 27, 2005 7:15
Location: FRANCE

Post by SARG »

Looking at the generated code, there is a jump skiping the loop just after the iterator initialization. In case of byte, uinteger datatype no jump.

Code: Select all

##for i as ubyte = 3 to 1 step -1
.Lt_0004:
	mov byte ptr [ebp-12], 3
	jmp .Lt_0007   <------------------------------------ JUMP
.Lt_0008:
.Lt_0009:
.stabn 68,0,4,.Lt_0033-_fb_ctor__testzfor
.Lt_0034:
##    print "ub ";i
	push 0
	push 3
	push offset _Lt_000A
	call _fb_StrAllocTempDescZEx@8
	push eax
	push 0
	call _fb_PrintString@12
	push 1
	push dword ptr [ebp-12]
	push 0
	call _fb_PrintUByte@12
.stabn 68,0,5,.Lt_0034-_fb_ctor__testzfor
.Lt_0035:
##next
.Lt_000B:
.Lt_0006:
	movzx eax, byte ptr [ebp-12]
	add eax, 255
	mov bl, al
	mov byte ptr [ebp-12], bl
.Lt_0005:
	movzx ebx, byte ptr [ebp-12]
	cmp ebx, 1
	jbe .Lt_0008
.Lt_0007:
.Lt_000C:
.stabn 68,0,6,.Lt_0035-_fb_ctor__testzfor
.Lt_0036:
##for i as byte = 3 to 1 step -1
.Lt_000D:
	mov byte ptr [ebp-12], 3
                                                                     <------ No jump
.Lt_0011:
.Lt_0012:
.stabn 68,0,7,.Lt_0036-_fb_ctor__testzfor
.Lt_0037:
##    print "by ";i
	push 0
	push 3
	push offset _Lt_0013
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Post by MichaelW »

Starting with this source:

Code: Select all

dim as integer x,y

for i as byte = 3 to 1 step -1
  x += 1
next
for i as ubyte = 3 to 1 step -1
  y += 1
next

print x, y

sleep
In the generated assembly it looks like the loop code for the unsigned iterator has been intentionally bypassed:
.intel_syntax noprefix
.section .text
.balign 16
.globl _main
_main:
push ebp
mov ebp, esp
and esp, 0xFFFFFFF0
sub esp, 16
push ebx
mov dword ptr [ebp-4], 0
call ___main
push 0
push dword ptr [ebp+12]
push dword ptr [ebp+8]
call _fb_Init@12
.Lt_0001:
mov dword ptr [ebp-8], 0
mov dword ptr [ebp-12], 0
mov byte ptr [ebp-16], 3
.Lt_0006:
inc dword ptr [ebp-8]
.Lt_0004:
movsx eax, byte ptr [ebp-16]
dec eax
mov bl, al
mov byte ptr [ebp-16], bl
.Lt_0003:
movsx ebx, byte ptr [ebp-16]
cmp ebx, 1
jge .Lt_0006
.Lt_0005:
mov byte ptr [ebp-16], 3
jmp .Lt_0009 /* bypass */
.Lt_000A:
inc dword ptr [ebp-12]
.Lt_0008:
movzx ebx, byte ptr [ebp-16]
add ebx, 255
mov al, bl
mov byte ptr [ebp-16], al
.Lt_0007:
movzx eax, byte ptr [ebp-16]
cmp eax, 1
jbe .Lt_000A
.Lt_0009:
push 2
push dword ptr [ebp-8]
push 0
call _fb_PrintInt@12
push 1
push dword ptr [ebp-12]
push 0
call _fb_PrintInt@12
push -1
call _fb_Sleep@4
.Lt_0002:
push 0
call _fb_End@4
mov eax, dword ptr [ebp-4]
pop ebx
mov esp, ebp
pop ebp
ret
Commenting out the jmp .Lt_0009 statement, and building the EXE directly from the assembly code, the loop runs two times instead of three. A total of three modifications were required to force the loop to run three times:
.intel_syntax noprefix
.section .text
.balign 16
.globl _main
_main:
push ebp
mov ebp, esp
and esp, 0xFFFFFFF0
sub esp, 16
push ebx
mov dword ptr [ebp-4], 0
call ___main
push 0
push dword ptr [ebp+12]
push dword ptr [ebp+8]
call _fb_Init@12
.Lt_0001:
mov dword ptr [ebp-8], 0
mov dword ptr [ebp-12], 0
mov byte ptr [ebp-16], 3
.Lt_0006:
inc dword ptr [ebp-8]
.Lt_0004:
movsx eax, byte ptr [ebp-16]
dec eax
mov bl, al
mov byte ptr [ebp-16], bl
.Lt_0003:
movsx ebx, byte ptr [ebp-16]
cmp ebx, 1
jge .Lt_0006
.Lt_0005:
mov byte ptr [ebp-16], 3
/*jmp .Lt_0009*/
.Lt_000A:
inc dword ptr [ebp-12]
.Lt_0008:
movzx ebx, byte ptr [ebp-16]
/*add ebx, 255*/
dec ebx
mov al, bl
mov byte ptr [ebp-16], al
.Lt_0007:
movzx eax, byte ptr [ebp-16]
cmp eax, 1
/*jbe .Lt_000A*/
jge .Lt_000A
.Lt_0009:
push 2
push dword ptr [ebp-8]
push 0
call _fb_PrintInt@12
push 1
push dword ptr [ebp-12]
push 0
call _fb_PrintInt@12
push -1
call _fb_Sleep@4
.Lt_0002:
push 0
call _fb_End@4
mov eax, dword ptr [ebp-4]
pop ebx
mov esp, ebp
pop ebp
ret
IMO, given that a QB FOR…NEXT can accept negative values for startvalue and endvalue, the use of an unsigned iterator is a logical error that FBC should report as an error. If it were my decision, I would disallow unsigned iterators as well as iterators smaller than 32 bits.
Post Reply