Skip to content

Commit

Permalink
Handle NaN comparisons in the interpreter (#14441)
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil authored Apr 8, 2024
1 parent c32e5a9 commit 8f1d45a
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 8 deletions.
4 changes: 4 additions & 0 deletions src/compiler/crystal/interpreter/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/crystal/interpreter/instructions.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
41 changes: 35 additions & 6 deletions src/compiler/crystal/interpreter/primitives.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
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
Expand Down

0 comments on commit 8f1d45a

Please sign in to comment.