Weird computation behaviour

Discussion of issues related specifically to writing code for native mode devices. This includes ZBasic code as well as assembly language code and C code, both inline and standalone.
Post Reply
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Weird computation behaviour

Post by wwwoholic »

I have ZX24n program that behaves really strange. Here is a simplified code, even though I think it won't do any good as it worked fine before

Code: Select all

dim CurSpeed as single = 0.0
public function ProcessCurves(byval target as integer) as integer
	dim speed as integer
	dim sChange as integer

	speed = CInt(CurSpeed)
	if (speed > 0) then
		sChange = target - speed
	elseif &#40;speed < 0&#41; then
		sChange = speed - target
	else
		sChange = Abs&#40;target&#41;
	end if

	if &#40;sChange < 0&#41; then
		Debug.Print "--- brake spd "; CStr&#40;CurSpeed&#41;; " t "; CStr&#40;target&#41;; " dV "; CStr&#40;dV&#41;;
	elseif &#40;sChange > 0&#41; then
		Debug.Print "--- accel spd "; CStr&#40;CurSpeed&#41;; " t "; CStr&#40;target&#41;; " dV "; CStr&#40;dV&#41;;
	else
		Debug.Print "--- keep spd "; CStr&#40;CurSpeed&#41;; " t "; CStr&#40;target&#41;
	end if
end function
The printout looks like this (note second line):
--- brake spd -64.10392 t 14 dV 3.735294
--- accel spd -60.31372 t -8 dV -3.527778
--- brake spd -8.0 t 0 dV 3.735294

So, I add debug like this:

Code: Select all

	elseif &#40;speed < 0&#41; then
		sChange = speed - target
		Debug.Print "- "; CStr&#40;sChange&#41;
	else
And the result is:
- 124
--- accel spd -126.0 t -125 dV -3.527778
- 76
--- accel spd -125.0 t -77 dV -3.527778
- -32
--- brake spd -77.0 t 31 dV 3.735294

Now it is really getting weird. Looks like "sChange = speed - target" instead does "sChange = -1 - target". So, I am changing debug format to more descriptive:

Code: Select all

Debug.Print CStr&#40;CurSpeed&#41;; " "; CStr&#40;speed&#41;; " - "; CStr&#40;target&#41;; " = "; CStr&#40;sChange&#41;
And suddenly program starts working properly! All the numbers are correct. I comment out Debug.Print line and the weird behavior is back. How can removal of debug output affect simple calculation so drastically?
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Weird computation behaviour

Post by dkinzer »

wwwoholic wrote:How can removal of debug output affect simple calculation so drastically?
It is very difficult to speculate as to what might be happening here. It may be an issue with how the code is optimized by the back-end compiler (or even by the ZBasic compiler itself). Some insight might be gleaned from the generated C source code (use the option --keep-files in the .pjt file).

It would be very helpful if you could construct a self-contained, minimal application that demonstrates the problem. This is sometimes difficult or even impossible to do. If necessary, we can usually work with your complete application.
- Don Kinzer
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Post by wwwoholic »

I actually did compare c code form working and non-working compilations. The only difference I could see is that new debug has been added, which consequently changed string offsets in all other debug lines. I'll try to make a simple test but I have a feeling it will work just fine.
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Post by wwwoholic »

OK, I managed to slim down the code to manageable 50 lines.

The strangest thing happens with debug output at lines 38, 39.
If you comment out 38 and un-comment 39 the program works as expected.
Attachments
scooter.bas
(1.26 KiB) Downloaded 681 times
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

wwwoholic wrote:I managed to slim down the code to manageable 50 lines.
Thanks for your effort, I was able to reproduce the issue here and discover that it is caused by bad code generation on the part of the back-end compiler. This is the first case I've seen yet where that version of avr-gcc (v4.3.3, WinAVR 20100110) emitted bad code. The problem is that the generated code trashes the value of the speed variable as part of the elseif (speed < 0) test.

I have determined empirically that you can work around the problem by rearranging the code as shown below.

Code: Select all

  if &#40;speed = 0&#41; then
    sChange = Abs&#40;target&#41;
'   Debug.Print "0 "; CStr&#40;sChange&#41;
  elseif &#40;speed > 0&#41; then
    sChange = target - speed
'   Debug.Print "+ "; CStr&#40;sChange&#41;
  else
    sChange = speed - target
    Debug.Print CStr&#40;sChange&#41;
'   Debug.Print CStr&#40;CurSpeed&#41;; " "; CStr&#40;speed&#41;; " - "; CStr&#40;target&#41;; " = "; CStr&#40;sChange&#41;
  end if
The current ZBasic compiler uses the v4.3.3 avr-gcc compiler for most devices; using v4.6.2 for some newer devices that aren't supported by the older version. I have confirmed that v4.6.2 does not emit the same bad code so we'll be switching over to use it (or a later version) for all devices.
- Don Kinzer
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Post by wwwoholic »

Wow! I suspected something like this after looking at generated C code but could not believe it. Hope switching to new compiler would not be too much of a pain for you.

Thanks for workaround.
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

dkinzer wrote:by bad code generation on the part of the back-end compiler. This is the first case I've seen yet where that version of avr-gcc (v4.3.3, WinAVR 20100110) emitted bad code.
Wow, I never had bad code from that version of GCC either. I just have WRITTEN lots of bad code! Not that I have done an enormous amount of GCC programming compared to some people. But I considered that version a "sweet spot", and I still use it. It seems to generate slightly more compact code than later versions which I find useful when I write or modify my bootloader. I am right at the limit of 1024 words and one extra byte makes a difference. Later versions of GCC add 2 bytes to something and the bootloader would require a larger reserved space. Not good for small devices.

I wonder if the newer version of GCC has a negative impact on the code for your bootloader? I expect that you use hand written ASM in a library for most of it, however. Maybe it will not be affected by any loss of efficiency of the newer compiler?
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

spamiam wrote: I never had bad code from that version of GCC either
Just for reference, the .lss file excerpt below shows the problem. The test for being less than zero is implemented by ORing the two bytes of the zv_speed value and then testing for zero (a previous test already determined that zv_speed was non-negative). The code then proceeds to use the r22-r23 register pair as if it still contained the original zv_speed value but, of course, it doesn't.

Code: Select all

	else if &#40;zv_speed < 0&#41;
 aa8&#58;	67 2b       	or	r22, r23
 aaa&#58;	41 f0       	breq	.+16
	&#123;
		zv_sChange = zv_speed - zp_target;
 aac&#58;	eb 01       	movw	r28, r22
 aae&#58;	c0 1b       	sub	r28, r16
 ab0&#58;	d1 0b       	sbc	r29, r17
spamiam wrote:I wonder if the newer version of GCC has a negative impact on the code for your bootloader?
The ZBasic compatible bootloader is written in assembly language partly to insulate it from changes in the code generator. For some devices, the bootloader code size is very near the boot size and a small increase might render it too big.
- Don Kinzer
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Post by wwwoholic »

Hmm... this does not explain why changing format in debug.print AFTER this check fixes the problem. I wonder how often weird program behavior in the past was due to this. Similar if-elseif pattern is fairly common in my code.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

wwwoholic wrote:Hmm... this does not explain why changing format in debug.print AFTER this check fixes the problem.
That's correct. It only explains why the code fails in the particular situation.

If you change the code, it can change the registers that the compiler uses to hold variables and/or change the ways that it uses them. If the second Debug.Print is used instead of the first, the following code is generated for the test and the difference calculation:

Code: Select all

	else if &#40;zv_speed < 0&#41;
    102a&#58;	61 15       	cp	r22, r1
    102c&#58;	71 05       	cpc	r23, r1
    102e&#58;	31 f1       	breq	.+76
	&#123;
		zv_sChange = zv_speed - zp_target;
    1030&#58;	eb 01       	movw	r28, r22
    1032&#58;	cc 19       	sub	r28, r12
    1034&#58;	dd 09       	sbc	r29, r13
Here, registers r22 and r23 (holding the speed variable) are compared to zero (register r1 always contains zero). That's why the code works in this case.
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

I've posted a new version of the installer that contains v4.2.0 of the ZBasic compiler, v4.2.0 of the ZBasic Library and v1.6.8 of the IDE. For the compiler, the main difference is that it now uses avr-gcc v4.7.2 (AVR_Toolchain v3.4.2) as the back-end compiler for all native mode devices. The ZBasic Library code was build using the same compiler.

Although the new compiler passes the testsuite, I'd like to get some real-world use before I post it on the Downloads page for general use. Please let me know of any issues that might arise.

http://www.zbasic.net/download/installe ... _4-2-0.exe
- Don Kinzer
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Post by wwwoholic »

Thanks! I'll give it a try on my test device. Can't promise real-world testing anytime soon though as my customer uses these in custom-built wheelchairs and for safety and liability reasons would rather wait for proven solution.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

wwwoholic wrote:I'll give it a try on my test device. Can't promise real-world testing anytime soon [...].
What I meant to refer to was compiling and testing applications other than those in our testsuite. If you compile your application, test it thoroughly and find that it works correctly that is a successful real-world test to us.
- Don Kinzer
Post Reply