# encoding: utf-8 # frozen_string_literal: true module Carbon module Core module Integer # Defines math operations on almost all integers except booleans. # Provides the following methods on all integer types except booleans: # # - `.+(self, T: other): self` # - `.-(self, T: other): self` # - `.*(self, T: other): self` # - `./(self, T: other): self` # - `.%(self, T: other): self` # - `.|(self, T: other): self` # - `.&(self, T: other): self` # - `.<<(self, T: other): self` # - `.>>(self, T: other): self` # - `.^(self, T: other): self` # - `.==(self, T: other): Carbon::Boolean` # - `.===(self, T: other): Carbon::Boolean` # - `.<(self, T: other): Carbon::Boolean` # - `.>(self, T: other): Carbon::Boolean` # - `.<=(self, T: other): Carbon::Boolean` # - `.>=(self, T: other): Carbon::Boolean` # # Note that the core library never actually uses generics. Instead, # all math operations are defined relative to another integer, and # overloading is used to match to the right function. For example, # `Int32.+(Int32, Int8)` is a different function than # `Int32.+(Int32, UInt8)`; the functions use no actual generics. # # @api private module Math # The math operations that can be performed. Note that for LLVM, # `:/`, `:%`, and `:>>` are sign-dependant. # # # @return [<::Symbol>] MATH_OPERATIONS = %i(+ - * / % | & << >> ^).freeze # The comparison operations that can be performed. Note that for # LLVM, `:<`, `:>`, `:<=`, and `:>=` are sign-dependant. # # @return [<::Symbol>] COMP_OPERATIONS = %i(== === < > <= >=).freeze # A mapping of comparison operations to their _icmp_ equivalents. # Since most of the _icmp_ values are sign-dependant, the keys also # contain sign information. # # @return [{(::Symbol, ::Symbol) => ::Symbol}] COMP_OPERATIONS_MAP = { [:unsigned, :==] => :eq, [:signed, :==] => :eq, [:unsigned, :===] => :eq, [:signed, :===] => :eq, [:unsigned, :<] => :ult, [:signed, :<] => :slt, [:unsigned, :>] => :ugt, [:signed, :>] => :sgt, [:unsigned, :<=] => :ule, [:signed, :<=] => :sle, [:unsigned, :>=] => :uge, [:signed, :>=] => :sge }.deep_freeze! # Defines all of the math functions. The left and right integer types # are given, and the {MATH_OPERATIONS} are iterated over, providing # the operator for the {#define_math_function} method. # # @param left [Core::Int] The left (receiver) value. # @param right [Core::Int] The right value. # @return [void] def define_math_functions(left, right) MATH_OPERATIONS.each { |op| define_math_function(left, right, op) } end # Defines all of the comparison functinos. The left and right integer # types are given, and the {COMP_OPERATIONS} are iterated over, # providing the operator for the {#define_comp_function} method. # # @param left [Core::Int] The left (receiver) value. # @param right [Core::Int] The right value. # @return [void] def define_comp_functions(left, right) COMP_OPERATIONS.each { |op| define_comp_function(left, right, op) } end # Defines a math function. The left and right integer types are given, # as well as the specific operation. # # @param left [Core::Int] The left (receiver) value. # @param right [Core::Int] The right value. # @param op [::Symbol] The mathmatical operation. One of # {MATH_OPERATIONS}. # @return [void] def define_math_function(left, right, op) function_name = left.name.call(op, [left.name, right.name]) Core.define(function: function_name) do |function| function[:return] = left.name define_math_definition(left, right, op, function[:definition]) end end # Defines a compare function. The left and right integer types are # given, as well as the specific operation. # # @param left [Core::Int] The left (receiver) value. # @param right [Core::Int] The right value. # @param op [::Symbol] The comparison operation. One of # {COMP_OPERATIONS}. # @return [void] def define_comp_function(left, right, op) function_name = left.name.call(op, [left.name, right.name]) Core.define(function: function_name) do |function| function[:return] = Carbon::Boolean define_comp_definition(left, right, op, function[:definition]) end end private def define_comp_definition(left, right, op, definition) entry = definition.add("entry").build params = definition.params params[0].name, params[1].name = %w(self other) this, other = force_same_cast(params, entry, left, right) icmp = COMP_OPERATIONS_MAP.fetch(left.sign, op) entry.ret(entry.icmp(icmp, this, other).as(Carbon::Boolean)) end def define_math_definition(left, right, op, definition) entry = definition.add("entry").build params = definition.params params[0].name, params[1].name = %w(self other) this, other = force_same_cast(params, entry, left, right) result = perform_operation(entry, [this, other], left, op) .as(this.type) return_original_size(entry, result, left, right) end OPERATIONS = { :+ => :add, :- => :sub, :| => :or, :& => :and, :* => :mul, :"^" => :xor, :<< => :shl }.freeze def perform_operation(entry, params, left, op) if OPERATIONS.key?(op) entry.public_send(OPERATIONS[op], *params) elsif op == :>> then right_shift(entry, params, left) elsif op == :/ then divide(entry, params, left) elsif op == :% then remainder(entry, params, left) else fail # Not possible. end end def right_shift(entry, params, left) case left.sign when :unsigned then entry.lshr(*params) when :signed then entry.ashr(*params) end end def divide(entry, params, left) case left.sign when :unsigned then entry.udiv(*params) when :signed then entry.sdiv(*params) end end def remainder(entry, params, left) case left.sign when :unsigned then entry.urem(*params) when :signed then entry.srem(*params) end end def return_original_size(entry, result, left, right) size = left.size <=> right.size return entry.ret(result) if size == 0 || size == 1 type = Int.find(size: right.size, sign: left.sign) fname = type.name.call(left.cast, [type.name]) entry.call(fname, result) end def force_same_cast(params, entry, left, right) dest = destination_size([left, right]) lcall, rcall = [left, right].map { |p| p.name.call(dest.cast, [p.name]) } [entry.call(lcall, params[0]).as(dest.name), entry.call(rcall, params[1]).as(dest.name)] end def destination_size(params) sign = params[0].sign size = params.map(&:size).max Int.find(size: size, sign: sign) end end end end end