freebasic / fbc

FreeBASIC is a completely free, open-source, multi-platform BASIC compiler, with syntax similar to MS-QuickBASIC, that adds new features such as pointers, object orientation, unsigned data types, inline assembly, and many others.
https://www.freebasic.net
905 stars 139 forks source link

if statement fail on div0 #410

Closed dafhi closed 1 year ago

dafhi commented 1 year ago

[update 2: simplified & run-time comment]

/'
  if fail: comparing .z * 1/0

  fbc -w all
'/

type v
  as single        z
  declare operator cast as string
End Type

operator v.cast  as string
  return str(z)
end operator

operator *(l as v,r as single) as v: return type(l.z*r): end operator

sub test( a as v, b as v )
  if a.z > b.z then
    var k = a.z > b.z
    ? a.z, b.z
    print "if statement thinks these are different"
    print
    print "assigned var inside block says they are equal (correct)" '' updated
    ? "a > b ?:"; k
  endif
End Sub

dim as v  a,b

a*=(1/0)
b*=(1/0)

test a,b

[old version]

/' 
  v3( all elems ) * 1/0
'/

type v3
  as single        x,y,z
  declare operator cast as string
End Type

operator v3.cast  as string
  return str(x) + " " + str(y) + " " + str(z)
end operator

operator *(l as v3,r as single) as v3: return type(l.x*r,l.y*r,l.z*r): end operator

' -------------------------------------
Type sort_type   as v3
' -------------------------------------

#define dot   .z

sub test(a() As sort_type)
  for J as long = 1 to Ubound(a)
    if a(J)dot > a(0)dot then
      var k = a(J)dot > a(0)dot
      ? a(j)dot, a(0)dot
      print "if statement thinks these are different"
      ? "a > b ?:"; k
    endif
  next
End Sub

var        u = 1
dim as v3  a(1)

dim as v3  dv(ubound(a))

for i as long = 0 to u
  dv(i) = a(i)*(1/0)
next

test dv()
jayrm commented 1 year ago

Following example should help provide a simplified starting point to debug:

sub doCompare( byval a as single, byval b as single )
    print "a:"; a, "b:"; b
    print "a = b    :"; cbool(a = b)
    print "a <> b   :"; cbool(a <> b)
    print "a > b    :"; cbool(a > b)
    print "a < b    :"; cbool(a < b)
    print "a >= b   :"; cbool(a >= b)
    print "a <= b   :"; cbool(a <= b)
    print
end sub

dim as single a = 1/0
dim as single b = 1/0

doCompare( a, b )

a *= 0
b *= 0

doCompare( a, b )

Results comaring gcc and gas backends (generated on win targets):

fbc version 1.10.0, 32-bit or 64-bit gcc backend

a: 1.#INF      b: 1.#INF
a = b    :true
a <> b   :false
a > b    :false
a < b    :false
a >= b   :true
a <= b   :true

a:-1.#IND     b:-1.#IND
a = b    :false
a <> b   :true
a > b    :false
a < b    :false
a >= b   :false
a <= b   :false

fbc version 1.10.0, 32-bit gas backend or 64-bit gas64 backend

a: 1.#INF      b: 1.#INF
a = b    :true
a <> b   :false
a > b    :false
a < b    :false
a >= b   :true
a <= b   :true

a:-1.#IND     b:-1.#IND
a = b    :false
a <> b   :true
a > b    :false
a < b    :true
a >= b   :false
a <= b   :true
dafhi commented 1 year ago

bug no longer appears even with my January MX Linux LiveCD w/ fbc 1.09 and Geany. methinks firmware was updated

dafhi commented 1 year ago

my bad. it's still there. forgot fbc -w all

jayrm commented 1 year ago

For gas 32-bit backend targeting 386, 486, 586, we get additional different results for #IND:

a: 1.#INF     b: 1.#INF
a = b    :true
a <> b   :false
a > b    :false
a < b    :false
a >= b   :true
a <= b   :true

a:-1.#IND     b:-1.#IND
a = b    :true
a <> b   :false
a > b    :false
a < b    :true
a >= b   :false
a <= b   :true
jayrm commented 1 year ago

After some investigation, here's kind of what is happening:

If we have some user code:

if( float rel-op float ) then
    true-statement
else
    false-statement
endif

where rel-op is one of =, <>, >, <, >=, <=.

This gets translated in the backend roughly to:

if( float inverse-rel-op ) then goto false-label
    true-statement
    goto exit-label
false-label:
    false-statement
exit-label

Where inverse-rel-op is the swapping of relation operators: = and <>, < and >=, > and <= Which should be fine for integer comparisons, but messes up on float comparisons since the expectation on NaN floats is not symmetrical in this way.

Initially (float rel-op float) returns an integer result either 0 or -1. This is then compared to zero and we get ((float rel-op float) = 0) to check for the false condition allowing a jump to the false label. Currently, fbc optimizes this in the AST as (float inverse-rel-op float), and then pass it on to the backend to deal with. Possibly we could solve in AST by never allowing this particular optimization but when trying this by disabling the optimization, fbc just crashes and it appears that the IR is not prepared to handle relation operations in this way. So currently is not really an optional optimization but a requirement so that AST/IR/BACKEND all work together. It is probably worth looking at what is going on here in more depth for understanding, but it is also probably worthwhile to hand off the optimization to the backend anyway.

Eventually, there are several areas that should be tested:

jayrm commented 1 year ago

resolved by #412