From 3707d798ef52ea3e7e3421e155669dd92047bf93 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Sat, 6 Apr 2024 15:58:58 +0800 Subject: [PATCH 1/2] Handle NaN comparisons in the interpreter --- src/compiler/crystal/interpreter/compiler.cr | 4 ++ .../crystal/interpreter/instructions.cr | 6 ++- .../crystal/interpreter/primitives.cr | 41 ++++++++++++++++--- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/compiler/crystal/interpreter/compiler.cr b/src/compiler/crystal/interpreter/compiler.cr index 04fc885fce7e..b25b6e7e25f9 100644 --- a/src/compiler/crystal/interpreter/compiler.cr +++ b/src/compiler/crystal/interpreter/compiler.cr @@ -3367,6 +3367,10 @@ class Crystal::Repl::Compiler < Crystal::Visitor @instructions.instructions << value end + private def append(value : Enum) + append(value.value) + end + # Many times we need to jump or branch to an instruction for which we don't # know the offset/index yet. # In those cases we generate a jump to zero, but remember where that "zero" diff --git a/src/compiler/crystal/interpreter/instructions.cr b/src/compiler/crystal/interpreter/instructions.cr index f2b8abcb9a51..804726beaad4 100644 --- a/src/compiler/crystal/interpreter/instructions.cr +++ b/src/compiler/crystal/interpreter/instructions.cr @@ -951,14 +951,16 @@ require "./repl" code: a == b ? 0 : (a < b ? -1 : 1), }, cmp_f32: { + operands: [predicate : Compiler::FloatPredicate], pop_values: [a : Float32, b : Float32], push: true, - code: a == b ? 0 : (a < b ? -1 : 1), + code: predicate.compare(a, b), }, cmp_f64: { + operands: [predicate : Compiler::FloatPredicate], pop_values: [a : Float64, b : Float64], push: true, - code: a == b ? 0 : (a < b ? -1 : 1), + code: predicate.compare(a, b), }, cmp_eq: { pop_values: [cmp : Int32], diff --git a/src/compiler/crystal/interpreter/primitives.cr b/src/compiler/crystal/interpreter/primitives.cr index 968361fb6c1d..bb17fe1ee736 100644 --- a/src/compiler/crystal/interpreter/primitives.cr +++ b/src/compiler/crystal/interpreter/primitives.cr @@ -1325,16 +1325,17 @@ class Crystal::Repl::Compiler end private def primitive_binary_op_cmp_float(node : ASTNode, kind : NumberKind, op : String) - case kind - when .f32? then cmp_f32(node: node) - when .f64? then cmp_f64(node: node) - else - node.raise "BUG: missing handling of binary #{op} with kind #{kind}" + if predicate = FloatPredicate.from_method?(op) + case kind + when .f32? then return cmp_f32(predicate, node: node) + when .f64? then return cmp_f64(predicate, node: node) + end end - primitive_binary_op_cmp_op(node, op) + node.raise "BUG: missing handling of binary #{op} with kind #{kind}" end + # TODO: should integer comparisons also use `FloatPredicate`? private def primitive_binary_op_cmp_op(node : ASTNode, op : String) case op when "==" then cmp_eq(node: node) @@ -1348,6 +1349,34 @@ class Crystal::Repl::Compiler end end + # interpreter-exclusive flags for `cmp_f32` and `cmp_f64` + # currently compatible with `LLVM::RealPredicate` + @[Flags] + enum FloatPredicate : UInt8 + Equal + GreaterThan + LessThan + Unordered + + def self.from_method?(op : String) : self + case op + when "==" then Equal + when "!=" then LessThan | GreaterThan | Unordered + when "<" then LessThan + when "<=" then LessThan | Equal + when ">" then GreaterThan + when ">=" then GreaterThan | Equal + end + end + + def compare(x, y) : Bool + (equal? && x == y) || + (greater_than? && x > y) || + (less_than? && x < y) || + (unordered? && (x.nan? || y.nan?)) + end + end + # interpreter-exclusive integer unions private enum MixedNumberKind # Int64 | UInt64 From 7313e60575b564390fe37f23f9c8fdcc2da71abe Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Sat, 6 Apr 2024 16:07:25 +0800 Subject: [PATCH 2/2] fixup --- src/compiler/crystal/interpreter/primitives.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/crystal/interpreter/primitives.cr b/src/compiler/crystal/interpreter/primitives.cr index bb17fe1ee736..16a0364be714 100644 --- a/src/compiler/crystal/interpreter/primitives.cr +++ b/src/compiler/crystal/interpreter/primitives.cr @@ -1358,7 +1358,7 @@ class Crystal::Repl::Compiler LessThan Unordered - def self.from_method?(op : String) : self + def self.from_method?(op : String) case op when "==" then Equal when "!=" then LessThan | GreaterThan | Unordered