# Copyright (C) 2021 Igalia S.L. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. # Naming conventions # # x => GPR, used to operate with 32-bit and 64-bit integer values # f => FPR, used to operate with 32-bit and 64-bit floating-point values # # GPR conventions, to match the baseline JIT: # # x0 => not used (except where needed for operations) (RISC-V hard-wired zero register) # x1 => la (through alias ra) (RISC-V return address register) # x2 => sp (through alias sp) (RISC-V stack pointer register) # x3 => not used (RISC-V global pointer register) # x4 => not used (RISC-V thread pointer register) # x5 => not used # x6 => ws0 # x7 => ws1 # x8 => cfr (thought alias fp) (RISC-V frame pointer register) # x9 => csr0 # x10 => t0, a0, wa0, r0 # x11 => t1, a1, wa1, r1 # x12 => t2, a2, wa2 # x13 => t3, a3, wa3 # x14 => t4, a4, wa4 # x15 => t5, a5, wa5 # x16 => t6, a6, wa6 # x17 => t7, a7, wa7 # x18 => csr1 # x19 => csr2 # x20 => csr3 # x21 => csr4 # x22 => csr5 # x23 => csr6 (metadataTable) # x24 => csr7 (PB) # x25 => csr8 (numberTag) # x26 => csr9 (notCellMask) # x27 => csr10 # x28 => scratch register # x29 => scratch register # x30 => scratch register # x31 => scratch register # # FPR conventions, to match the baseline JIT: # # f0 => ft0 # f1 => ft1 # f2 => ft2 # f3 => ft3 # f4 => ft4 # f5 => ft5 # f6 => not used # f7 => not used # f8 => csfr0 # f9 => csfr1 # f10 => fa0, wfa0 # f11 => fa1, wfa1 # f12 => fa2, wfa2 # f13 => fa3, wfa3 # f14 => fa4, wfa4 # f15 => fa5, wfa5 # f16 => fa6, wfa6 # f17 => fa7, wfa7 # f18 => csfr2 # f19 => csfr3 # f20 => csfr4 # f21 => csfr5 # f22 => csfr6 # f23 => csfr7 # f24 => csfr8 # f25 => csfr9 # f26 => csfr10 # f27 => csfr11 # f28 => scratch register # f29 => scratch register # f30 => scratch register # f31 => scratch register RISCV64_EXTRA_GPRS = [SpecialRegister.new("x28"), SpecialRegister.new("x29"), SpecialRegister.new("x30"), SpecialRegister.new("x31")] RISCV64_EXTRA_FPRS = [SpecialRegister.new("f28"), SpecialRegister.new("f29"), SpecialRegister.new("f30"), SpecialRegister.new("f31")] def riscv64OperandTypes(operands) return operands.map { |op| if op.is_a? SpecialRegister case op.name when /^x/ RegisterID when /^f/ FPRegisterID else raise "Invalid SpecialRegister operand #{op.name}" end elsif op.is_a? Tmp case op.kind when :gpr RegisterID when :fpr FPRegisterID else raise "Invalid Tmp operand #{op.kind}" end else op.class end } end def riscv64RaiseMismatchedOperands(operands) raise "Unable to match operands #{riscv64OperandTypes(operands)}" end def riscv64ValidateOperands(operands, *expected) riscv64RaiseMismatchedOperands(operands) unless expected.include? riscv64OperandTypes(operands) end def riscv64ValidateImmediate(validation, value) case validation when :i_immediate (-0x800..0x7ff).include? value when :any_immediate true when :rv32_shift_immediate (0..31).include? value when :rv64_shift_immediate (0..63).include? value else raise "Invalid immediate validation #{validation}" end end class RegisterID def riscv64Operand case @name when 't0', 'a0', 'wa0', 'r0' 'x10' when 't1', 'a1', 'wa1', 'r1' 'x11' when 't2', 'a2', 'wa2' 'x12' when 't3', 'a3', 'wa3' 'x13' when 't4', 'a4', 'wa4' 'x14' when 't5', 'a5', 'wa5' 'x15' when 't6', 'a6', 'wa6' 'x16' when 't7', 'a7', 'wa7' 'x17' when 'ws0' 'x6' when 'ws1' 'x7' when 'csr0' 'x9' when 'csr1' 'x18' when 'csr2' 'x19' when 'csr3' 'x20' when 'csr4' 'x21' when 'csr5' 'x22' when 'csr6' 'x23' when 'csr7' 'x24' when 'csr8' 'x25' when 'csr9' 'x26' when 'csr10' 'x27' when 'lr' 'ra' when 'sp' 'sp' when 'cfr' 'fp' else raise "Bad register name #{@name} at #{codeOriginString}" end end end class FPRegisterID def riscv64Operand case @name when 'ft0' 'f0' when 'ft1' 'f1' when 'ft2' 'f2' when 'ft3' 'f3' when 'ft4' 'f4' when 'ft5' 'f5' when 'csfr0' 'f8' when 'csfr1' 'f9' when 'fa0', 'wfa0' 'f10' when 'fa1', 'wfa1' 'f11' when 'fa2', 'wfa2' 'f12' when 'fa3', 'wfa3' 'f13' when 'fa4', 'wfa4' 'f14' when 'fa5', 'wfa5' 'f15' when 'fa6', 'wfa6' 'f16' when 'fa7', 'wfa7' 'f17' when 'csfr2' 'f18' when 'csfr3' 'f19' when 'csfr4' 'f20' when 'csfr5' 'f21' when 'csfr6' 'f22' when 'csfr7' 'f23' when 'csfr8' 'f24' when 'csfr9' 'f25' when 'csfr10' 'f26' when 'csfr11' 'f27' else raise "Bad register name #{@name} at #{codeOriginString}" end end end class SpecialRegister def riscv64Operand @name end end class Immediate def riscv64Operand(validation = :i_immediate) raise "Invalid immediate value #{value} at #{codeOriginString}" if riscv64RequiresLoad(validation) "#{value}" end def riscv64RequiresLoad(validation = :i_immediate) not riscv64ValidateImmediate(validation, value) end end class Address def riscv64Operand raise "Invalid offset #{offset.value} at #{codeOriginString}" if riscv64RequiresLoad "#{offset.value}(#{base.riscv64Operand})" end def riscv64RequiresLoad not riscv64ValidateImmediate(:i_immediate, offset.value) end end class RISCV64RoundingMode < NoChildren def initialize(mode) @mode = mode end def riscv64RoundingMode case @mode when :floor "rdn" when :ceil "rup" when :round "rne" when :truncate "rtz" else raise "Invalid rounding mode #{@mode}" end end end class RISCV64MemoryOrdering < NoChildren def initialize(ordering) @ordering = ordering end def riscv64MemoryOrdering case @ordering when :rw, :iorw @ordering.to_s else raise "Invalid memory ordering #{@ordering}" end end end def riscv64LowerEmitMask(newList, node, size, source, destination) case size when :b, :h, :i case size when :b shiftSize = 56 when :h shiftSize = 48 when :i shiftSize = 32 end newList << Instruction.new(node.codeOrigin, "rv_slli", [source, Immediate.new(node.codeOrigin, shiftSize), destination]) newList << Instruction.new(node.codeOrigin, "rv_srli", [destination, Immediate.new(node.codeOrigin, shiftSize), destination]) when :p, :q else raise "Invalid masking size" end end def riscv64LowerEmitSignExtension(newList, node, size, source, destination) case size when :b, :h case size when :b shiftSize = 56 when :h shiftSize = 32 end newList << Instruction.new(node.codeOrigin, "rv_slli", [source, Immediate.new(node.codeOrigin, shiftSize), destination]) newList << Instruction.new(node.codeOrigin, "rv_srai", [destination, Immediate.new(node.codeOrigin, shiftSize), destination]) when :i newList << Instruction.new(node.codeOrigin, "rv_sext.w", [source, destination]) when :p, :q else raise "Invalid extension size" end end def riscv64LowerOperandIntoRegister(newList, node, operand) register = operand if operand.immediate? register = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_li", [operand, register]) end raise "Invalid register type" unless riscv64OperandTypes([register]) == [RegisterID] register end def riscv64LowerOperandIntoRegisterAndSignExtend(newList, node, operand, size, forcedTmp = :none) source = riscv64LowerOperandIntoRegister(newList, node, operand) destination = source if ([:b, :h, :i].include? size or forcedTmp == :forced_tmp) and not destination.is_a? Tmp destination = Tmp.new(node.codeOrigin, :gpr) end riscv64LowerEmitSignExtension(newList, node, size, source, destination) destination end def riscv64LowerMisplacedAddresses(list) newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when /^b(add|sub)i(z|nz|s)$/ case riscv64OperandTypes(node.operands) when [Immediate, Address, LocalLabelReference] tmp = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "loadi", [node.operands[1], tmp]) newList << Instruction.new(node.codeOrigin, "#{$1}i", [tmp, node.operands[0], tmp]) newList << Instruction.new(node.codeOrigin, "storei", [tmp, node.operands[1]]) newList << Instruction.new(node.codeOrigin, "bti#{$2}", [tmp, node.operands[2]]) else newList << node end else newList << node end else newList << node end } newList end def riscv64LowerAddressLoads(list) newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when "leap", "leaq" case riscv64OperandTypes(node.operands) when [Address, RegisterID] address, dest = node.operands[0], node.operands[1] raise "Invalid address" if address.riscv64RequiresLoad newList << Instruction.new(node.codeOrigin, "rv_addi", [address.base, address.offset, dest]) when [BaseIndex, RegisterID] bi, dest = node.operands[0], node.operands[1] newList << Instruction.new(node.codeOrigin, "rv_slli", [bi.index, Immediate.new(node.codeOrigin, bi.scaleShift), dest]) newList << Instruction.new(node.codeOrigin, "rv_add", [dest, bi.base, dest]) if bi.offset.value != 0 offset = Immediate.new(node.codeOrigin, bi.offset.value) if offset.riscv64RequiresLoad tmp = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_li", [offset, tmp]) newList << Instruction.new(node.codeOrigin, "rv_add", [dest, tmp, dest]) else newList << Instruction.new(node.codeOrigin, "rv_addi", [dest, offset, dest]) end end when [LabelReference, RegisterID] label, dest = node.operands[0], node.operands[1] newList << Instruction.new(node.codeOrigin, "rv_lla", [label, dest]) if label.offset != 0 offset = Immediate.new(node.codeOrigin, label.offset) if offset.riscv64RequiresLoad tmp = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_li", [offset, tmp]) newList << Instruction.new(node.codeOrigin, "rv_add", [dest, tmp, dest]) else newList << Instruction.new(node.codeOrigin, "rv_addi", [dest, offset, dest]) end end else riscv64RaiseMismatchedOperands(node.operands) end when "globaladdr" riscv64ValidateOperands(node.operands, [LabelReference, RegisterID]) newList << Instruction.new(node.codeOrigin, "rv_la", node.operands) when "pcrtoaddr" riscv64ValidateOperands(node.operands, [LabelReference, RegisterID]) newList << Instruction.new(node.codeOrigin, "rv_lla", node.operands) else newList << node end else newList << node end } newList end def riscv64LowerImmediateSubtraction(list) def emit(newList, node, size, operands) riscv64ValidateOperands(operands, [RegisterID, Immediate, RegisterID]) nimmediate = Immediate.new(node.codeOrigin, -operands[1].value) if nimmediate.riscv64RequiresLoad tmp = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_li", [operands[1], tmp]) newList << Instruction.new(node.codeOrigin, "rv_sub", [operands[0], tmp, operands[2]]) else newList << Instruction.new(node.codeOrigin, "rv_addi", [operands[0], nimmediate, operands[2]]) end riscv64LowerEmitMask(newList, node, size, operands[2], operands[2]) end newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when /^sub(i|p|q)$/ case riscv64OperandTypes(node.operands) when [RegisterID, Immediate, RegisterID] emit(newList, node, $1.to_sym, node.operands) when [Immediate, RegisterID] emit(newList, node, $1.to_sym, [node.operands[1], node.operands[0], node.operands[1]]) else raise "Invalid immediate subtraction pattern" if riscv64OperandTypes(node.operands).include? Immediate newList << node end else newList << node end else newList << node end } newList end def riscv64LowerOperation(list) def emitLoadOperation(newList, node, size) riscv64ValidateOperands(node.operands, [Address, RegisterID]) case size when :b suffix = "bu" when :bsi, :bsq suffix = "b" when :h suffix = "hu" when :hsi, :hsq suffix = "h" when :i suffix = "wu" when :is suffix = "w" when :p, :q suffix = "d" else raise "Invalid size #{size}" end newList << Instruction.new(node.codeOrigin, "rv_l#{suffix}", node.operands) case size when :bsi, :hsi riscv64LowerEmitMask(newList, node, :i, node.operands[1], node.operands[1]) when :bsq, :hsq # Nothing to do end end def emitStoreOperation(newList, node, size) riscv64ValidateOperands(node.operands, [RegisterID, Address]) case size when :b suffix = "b" when :h suffix = "h" when :i suffix = "w" when :p, :q suffix = "d" else raise "Invalid size #{size}" end newList << Instruction.new(node.codeOrigin, "rv_s#{suffix}", node.operands) end def emitMove(newList, node) case riscv64OperandTypes(node.operands) when [RegisterID, RegisterID] moveOpcode = "mv" when [Immediate, RegisterID] moveOpcode = "li" else riscv64RaiseMismatchedOperands(node.operands) end newList << Instruction.new(node.codeOrigin, "rv_#{moveOpcode}", node.operands) end def emitJump(newList, node) case riscv64OperandTypes(node.operands) when [RegisterID] jumpOpcode = "jr" when [LabelReference], [LocalLabelReference] jumpOpcode = "tail" else riscv64RaiseMismatchedOperands(node.operands) end newList << Instruction.new(node.codeOrigin, "rv_#{jumpOpcode}", node.operands) end def emitCall(newList, node) case riscv64OperandTypes(node.operands) when [RegisterID] callOpcode = "jalr" when [LabelReference] callOpcode = "call" else riscv64RaiseMismatchedOperands(node.operands) end newList << Instruction.new(node.codeOrigin, "rv_#{callOpcode}", node.operands) end def emitPush(newList, node) sp = RegisterID.forName(node.codeOrigin, 'sp') size = 8 * node.operands.size newList << Instruction.new(node.codeOrigin, "rv_addi", [sp, Immediate.new(node.codeOrigin, -size), sp]) node.operands.reverse.each_with_index { | op, index | offset = size - 8 * (index + 1) newList << Instruction.new(node.codeOrigin, "rv_sd", [op, Address.new(node.codeOrigin, sp, Immediate.new(node.codeOrigin, offset))]) } end def emitPop(newList, node) sp = RegisterID.forName(node.codeOrigin, 'sp') size = 8 * node.operands.size node.operands.each_with_index { | op, index | offset = size - 8 * (index + 1) newList << Instruction.new(node.codeOrigin, "rv_ld", [Address.new(node.codeOrigin, sp, Immediate.new(node.codeOrigin, offset)), op]) } newList << Instruction.new(node.codeOrigin, "rv_addi", [sp, Immediate.new(node.codeOrigin, size), sp]) end def emitAdditionOperation(newList, node, operation, size) operands = node.operands if operands.size == 2 operands = [operands[1], operands[0], operands[1]] end if riscv64OperandTypes(operands) == [Immediate, RegisterID, RegisterID] raise "Invalid subtraction pattern" if operation == :sub operands = [operands[1], operands[0], operands[2]] end riscv64ValidateOperands(operands, [RegisterID, RegisterID, RegisterID], [RegisterID, Immediate, RegisterID]) case operation when :add, :sub additionOpcode = operation.to_s else raise "Invalid operation #{operation}" end raise "Invalid subtraction of immediate" if operands[1].is_a? Immediate and operation == :sub additionOpcode += ((operands[1].is_a? Immediate) ? "i" : "") + (size == :i ? "w" : "") newList << Instruction.new(node.codeOrigin, "rv_#{additionOpcode}", operands) riscv64LowerEmitMask(newList, node, size, operands[2], operands[2]) end def emitMultiplicationOperation(newList, node, operation, size, signedness) operands = node.operands if operands.size == 2 operands = [operands[1], operands[0], operands[1]] end if riscv64OperandTypes(operands) == [Immediate, RegisterID, RegisterID] raise "Invalid division/remainder pattern" if [:div, :rem].include? operation operands = [operands[1], operands[0], operands[2]] end riscv64ValidateOperands(operands, [RegisterID, RegisterID, RegisterID], [RegisterID, Immediate, RegisterID]) case operation when :mul multiplicationOpcode = "mul" when :div, :rem multiplicationOpcode = operation.to_s + (signedness != :s ? "u" : "") else raise "Invalid operation #{operation}" end multiplicationOpcode += (size == :i ? "w" : "") newList << Instruction.new(node.codeOrigin, "rv_#{multiplicationOpcode}", operands) riscv64LowerEmitMask(newList, node, size, operands[2], operands[2]) end def emitShiftOperation(newList, node, operation, size) operands = node.operands if operands.size == 2 operands = [operands[1], operands[0], operands[1]] end riscv64ValidateOperands(operands, [RegisterID, RegisterID, RegisterID], [RegisterID, Immediate, RegisterID]) case operation when :l shiftOpcode = "sll" when :r shiftOpcode = "sra" when :ur shiftOpcode = "srl" else raise "Invalid operation #{operation}" end shiftOpcode += ((operands[1].is_a? Immediate) ? "i" : "") + (size == :i ? "w" : "") newList << Instruction.new(node.codeOrigin, "rv_#{shiftOpcode}", operands) riscv64LowerEmitMask(newList, node, size, operands[2], operands[2]) end def emitLogicalOperation(newList, node, operation, size) operands = node.operands if operands.size == 2 operands = [operands[1], operands[0], operands[1]] end riscv64ValidateOperands(operands, [RegisterID, RegisterID, RegisterID], [RegisterID, Immediate, RegisterID]) case operation when :and, :or, :xor logicalOpcode = operation.to_s else raise "Invalid operation #{operation}" end if operands[1].is_a? Immediate logicalOpcode += "i" end newList << Instruction.new(node.codeOrigin, "rv_#{logicalOpcode}", operands) riscv64LowerEmitMask(newList, node, size, operands[2], operands[2]) end def emitComplementOperation(newList, node, operation, size) riscv64ValidateOperands(node.operands, [RegisterID]) case operation when :neg complementOpcode = size == :i ? "negw" : "neg" when :not complementOpcode = "not" else raise "Invalid operation #{operation}" end newList << Instruction.new(node.codeOrigin, "rv_#{complementOpcode}", [node.operands[0], node.operands[0]]) riscv64LowerEmitMask(newList, node, size, node.operands[0], node.operands[0]) end def emitBitExtensionOperation(newList, node, extension, fromSize, toSize) raise "Invalid operand types" unless riscv64OperandTypes(node.operands) == [RegisterID, RegisterID] if [[:s, :i, :p], [:s, :i, :q]].include? [extension, fromSize, toSize] newList << Instruction.new(node.codeOrigin, "rv_sext.w", node.operands) return end source = node.operands[0] dest = node.operands[1] if [[:z, :i, :p], [:z, :i, :q]].include? [extension, fromSize, toSize] newList << Instruction.new(node.codeOrigin, "rv_slli", [source, Immediate.new(node.codeOrigin, 32), dest]) newList << Instruction.new(node.codeOrigin, "rv_srli", [dest, Immediate.new(node.codeOrigin, 32), dest]) return end raise "Invalid zero extension" unless extension == :s case [fromSize, toSize] when [:b, :i] newList << Instruction.new(node.codeOrigin, "rv_slli", [source, Immediate.new(node.codeOrigin, 56), dest]) newList << Instruction.new(node.codeOrigin, "rv_srai", [dest, Immediate.new(node.codeOrigin, 24), dest]) newList << Instruction.new(node.codeOrigin, "rv_srli", [dest, Immediate.new(node.codeOrigin, 32), dest]) when [:b, :q] newList << Instruction.new(node.codeOrigin, "rv_slli", [source, Immediate.new(node.codeOrigin, 56), dest]) newList << Instruction.new(node.codeOrigin, "rv_srai", [dest, Immediate.new(node.codeOrigin, 56), dest]) when [:h, :i] newList << Instruction.new(node.codeOrigin, "rv_slli", [source, Immediate.new(node.codeOrigin, 48), dest]) newList << Instruction.new(node.codeOrigin, "rv_srai", [dest, Immediate.new(node.codeOrigin, 16), dest]) newList << Instruction.new(node.codeOrigin, "rv_srli", [dest, Immediate.new(node.codeOrigin, 32), dest]) when [:h, :q] newList << Instruction.new(node.codeOrigin, "rv_slli", [source, Immediate.new(node.codeOrigin, 48), dest]) newList << Instruction.new(node.codeOrigin, "rv_srai", [dest, Immediate.new(node.codeOrigin, 48), dest]) else raise "Invalid bit-extension combination" end end newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when /^load(b|bsi|bsq|h||hsi|hsq|i|is|p|q)$/ emitLoadOperation(newList, node, $1.to_sym) when /^store(b|h|i|p|q)$/ emitStoreOperation(newList, node, $1.to_sym) when "move" emitMove(newList, node) when "jmp" emitJump(newList, node) when "call" emitCall(newList, node) when "push" emitPush(newList, node) when "pop" emitPop(newList, node) when /^(add|sub)(i|p|q)$/ emitAdditionOperation(newList, node, $1.to_sym, $2.to_sym) when /^(mul|div|rem)(i|p|q)(s?)$/ emitMultiplicationOperation(newList, node, $1.to_sym, $2.to_sym, $3.to_sym) when /^(l|r|ur)shift(i|p|q)$/ emitShiftOperation(newList, node, $1.to_sym, $2.to_sym) when /^(and|or|xor)(h|i|p|q)$/ emitLogicalOperation(newList, node, $1.to_sym, $2.to_sym) when /^(neg|not)(i|p|q)$/ emitComplementOperation(newList, node, $1.to_sym, $2.to_sym) when /^(s|z)x(b|h|i)2(i|p|q)$/ emitBitExtensionOperation(newList, node, $1.to_sym, $2.to_sym, $3.to_sym) when "break" newList << Instruction.new(node.codeOrigin, "rv_ebreak", []) when "nop", "ret" newList << Instruction.new(node.codeOrigin, "rv_#{node.opcode}", []) when "memfence" newList << Instruction.new(node.codeOrigin, "rv_fence", [RISCV64MemoryOrdering.new(:rw), RISCV64MemoryOrdering.new(:rw)]) when "fence" newList << Instruction.new(node.codeOrigin, "rv_fence", [RISCV64MemoryOrdering.new(:iorw), RISCV64MemoryOrdering.new(:iorw)]) else newList << node end else newList << node end } newList end def riscv64LowerTest(list) def branchOpcode(test) case test when :s "bltz" when :z "beqz" when :nz "bnez" else raise "Invalid test-branch opcode" end end def setOpcode(test) case test when :s "sltz" when :z "seqz" when :nz "snez" else raise "Invalid test-set opcode" end end def emit(newList, node, size, opcode) if node.operands.size == 2 newList << Instruction.new(node.codeOrigin, "rv_#{opcode}", node.operands) return end if node.operands[0].immediate? and node.operands[0].value == -1 newList << Instruction.new(node.codeOrigin, "rv_#{opcode}", [node.operands[1], node.operands[2]]) return end if node.operands[1].immediate? and node.operands[1].value == -1 newList << Instruction.new(node.codeOrigin, "rv_#{opcode}", [node.operands[0], node.operands[2]]) return end value = node.operands[0] mask = node.operands[1] if node.operands[0].immediate? value = node.operands[1] mask = node.operands[0] end tmp = Tmp.new(node.codeOrigin, :gpr) if value.register? and mask.register? newList << Instruction.new(node.codeOrigin, "rv_and", [value, mask, tmp]) else newList << Instruction.new(node.codeOrigin, "rv_li", [mask, tmp]); newList << Instruction.new(node.codeOrigin, "rv_and", [tmp, value, tmp]); end riscv64LowerEmitSignExtension(newList, node, size, tmp, tmp) newList << Instruction.new(node.codeOrigin, "rv_#{opcode}", [tmp, node.operands[2]]) end newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when /^bt(b|i|p|q)(s|z|nz)$/ emit(newList, node, $1.to_sym, branchOpcode($2.to_sym)) when /^t(b|i|p|q)(s|z|nz)$/ emit(newList, node, $1.to_sym, setOpcode($2.to_sym)) else newList << node end else newList << node end } newList end def riscv64LowerCompare(list) def emit(newList, node, size, comparison) lhs = riscv64LowerOperandIntoRegisterAndSignExtend(newList, node, node.operands[0], size) rhs = riscv64LowerOperandIntoRegisterAndSignExtend(newList, node, node.operands[1], size) dest = node.operands[2] case comparison when :eq tmp = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_sub", [lhs, rhs, tmp]) newList << Instruction.new(node.codeOrigin, "rv_seqz", [tmp, dest]) when :neq tmp = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_sub", [lhs, rhs, tmp]) newList << Instruction.new(node.codeOrigin, "rv_snez", [tmp, dest]) when :a newList << Instruction.new(node.codeOrigin, "rv_sltu", [rhs, lhs, dest]) when :aeq newList << Instruction.new(node.codeOrigin, "rv_sltu", [lhs, rhs, dest]) newList << Instruction.new(node.codeOrigin, "rv_xori", [dest, Immediate.new(node.codeOrigin, 1), dest]) when :b newList << Instruction.new(node.codeOrigin, "rv_sltu", [lhs, rhs, dest]) when :beq newList << Instruction.new(node.codeOrigin, "rv_sltu", [rhs, lhs, dest]) newList << Instruction.new(node.codeOrigin, "rv_xori", [dest, Immediate.new(node.codeOrigin, 1), dest]) when :gt newList << Instruction.new(node.codeOrigin, "rv_slt", [rhs, lhs, dest]) when :gteq newList << Instruction.new(node.codeOrigin, "rv_slt", [lhs, rhs, dest]) newList << Instruction.new(node.codeOrigin, "rv_xori", [dest, Immediate.new(node.codeOrigin, 1), dest]) when :lt newList << Instruction.new(node.codeOrigin, "rv_slt", [lhs, rhs, dest]) when :lteq newList << Instruction.new(node.codeOrigin, "rv_slt", [rhs, lhs, dest]) newList << Instruction.new(node.codeOrigin, "rv_xori", [dest, Immediate.new(node.codeOrigin, 1), dest]) else raise "Invalid comparison #{comparison}" end end newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when /^c(b|i|p|q)(eq|neq|a|aeq|b|beq|gt|gteq|lt|lteq)$/ emit(newList, node, $1.to_sym, $2.to_sym) else newList << node end else newList << node end } newList end def riscv64LowerBranch(list) def branchOpcode(condition) case condition when :eq "beq" when :neq "bne" when :a "bgtu" when :aeq "bgeu" when :b "bltu" when :beq "bleu" when :gt "bgt" when :gteq "bge" when :lt "blt" when :lteq "ble" when :z "beqz" when :nz "bnez" when :s "bltz" else raise "Invalid condition #{condition}" end end def emitGeneric(newList, node, size, condition) lhs = riscv64LowerOperandIntoRegisterAndSignExtend(newList, node, node.operands[0], size) rhs = riscv64LowerOperandIntoRegisterAndSignExtend(newList, node, node.operands[1], size) dest = node.operands[2] newList << Instruction.new(node.codeOrigin, "rv_#{branchOpcode(condition)}", [lhs, rhs, dest]) end def emitAddition(newList, node, operation, size, condition) operands = node.operands if operands.size == 3 operands = [operands[1], operands[0], operands[1], operands[2]] end riscv64ValidateOperands(operands, [RegisterID, RegisterID, RegisterID, LocalLabelReference], [RegisterID, Immediate, RegisterID, LocalLabelReference]); case operation when :add, :sub additionOpcode = operation.to_s + (size == :i ? "w" : "") else raise "Invalid addition operation" end lhs = riscv64LowerOperandIntoRegister(newList, node, operands[0]) rhs = riscv64LowerOperandIntoRegister(newList, node, operands[1]) newList << Instruction.new(node.codeOrigin, "rv_#{additionOpcode}", [lhs, rhs, operands[2]]) tmp = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_mv", [operands[2], tmp]) riscv64LowerEmitMask(newList, node, size, operands[2], operands[2]) newList << Instruction.new(node.codeOrigin, "rv_#{branchOpcode(condition)}", [tmp, operands[3]]) end def emitMultiplication(newList, node, size, condition) raise "Invalid size" unless size == :i lhs = result = riscv64LowerOperandIntoRegisterAndSignExtend(newList, node, node.operands[0], size, :forced_tmp) rhs = riscv64LowerOperandIntoRegisterAndSignExtend(newList, node, node.operands[1], size, :forced_tmp) raise "Invalid lowered-operand type" unless result.is_a? Tmp newList << Instruction.new(node.codeOrigin, "rv_mul", [lhs, rhs, result]) riscv64LowerEmitMask(newList, node, size, result, node.operands[1]) newList << Instruction.new(node.codeOrigin, "rv_#{branchOpcode(condition)}", [result, node.operands[2]]) end def emitOverflow(newList, node, operation, size) raise "Invalid size" unless size == :i operands = node.operands if operands.size == 3 operands = [operands[1], operands[0], operands[1], operands[2]] end riscv64ValidateOperands(operands, [RegisterID, RegisterID, RegisterID, LocalLabelReference], [RegisterID, Immediate, RegisterID, LocalLabelReference]); case operation when :add, :sub, :mul operationOpcode = operation.to_s else raise "Invalid operation #{operation}" end lhs = tmp1 = riscv64LowerOperandIntoRegisterAndSignExtend(newList, node, operands[0], size, :forced_tmp) rhs = tmp2 = riscv64LowerOperandIntoRegisterAndSignExtend(newList, node, operands[1], size, :forced_tmp) raise "Invalid lowered-operand type" unless (tmp1.is_a? Tmp and tmp2.is_a? Tmp) newList << Instruction.new(node.codeOrigin, "rv_#{operationOpcode}", [lhs, rhs, tmp1]) riscv64LowerEmitMask(newList, node, size, tmp1, operands[2]) newList << Instruction.new(node.codeOrigin, "rv_sext.w", [tmp1, tmp2]) newList << Instruction.new(node.codeOrigin, "rv_bne", [tmp1, tmp2, operands[3]]) end newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when /^b(b|i|p|q)(eq|neq|a|aeq|b|beq|gt|gteq|lt|lteq)$/ emitGeneric(newList, node, $1.to_sym, $2.to_sym) when /^b(add|sub)(i|p|q)(z|nz|s)$/ emitAddition(newList, node, $1.to_sym, $2.to_sym, $3.to_sym) when /^bmul(i)(z|nz|s)$/ emitMultiplication(newList, node, $1.to_sym, $2.to_sym) when /^b(add|sub|mul)(i)o$/ emitOverflow(newList, node, $1.to_sym, $2.to_sym) else newList << node end else newList << node end } newList end def riscv64LowerFPOperation(list) def emitLoadOperation(newList, node, precision) riscv64ValidateOperands(node.operands, [Address, FPRegisterID]) case precision when :f suffix = "w" when :d suffix = "d" else raise "Invalid precision #{precision}" end newList << Instruction.new(node.codeOrigin, "rv_fl#{suffix}", node.operands) end def emitStoreOperation(newList, node, precision) riscv64ValidateOperands(node.operands, [FPRegisterID, Address]) case precision when :f suffix = "w" when :d suffix = "d" else raise "Invalid precision #{precision}" end newList << Instruction.new(node.codeOrigin, "rv_fs#{suffix}", node.operands) end def emitMoveOperation(newList, node, precision) riscv64ValidateOperands(node.operands, [FPRegisterID, FPRegisterID]) raise "Invalid precision" unless [:f, :d].include? precision if precision == :f precision = :s end newList << Instruction.new(node.codeOrigin, "rv_fmv.#{precision.to_s}", node.operands) end def emitCopyOperation(newList, node, sourceType, destinationType) def registerType(type) case type when :i, :p, :q RegisterID when :f, :d FPRegisterID end end def fpSuffix(type) case type when :f "w" when :d "d" end end riscv64ValidateOperands(node.operands, [registerType(sourceType), registerType(destinationType)]) case riscv64OperandTypes(node.operands) when [RegisterID, FPRegisterID] fmvOpcode = "rv_fmv.#{fpSuffix(destinationType)}.x" when [FPRegisterID, RegisterID] fmvOpcode = "rv_fmv.x.#{fpSuffix(sourceType)}" else riscv64RaiseMismatchedOperands end newList << Instruction.new(node.codeOrigin, fmvOpcode, node.operands) end def emitComputationalOperation(newList, node, operation, precision) riscv64ValidateOperands(node.operands, [FPRegisterID, FPRegisterID]) raise "Invalid operation" unless [:add, :sub, :mul, :div, :sqrt, :abs, :neg].include? operation raise "Invalid precision" unless [:f, :d].include? precision if precision == :f precision = :s end operands = [node.operands[0], node.operands[1]] if [:add, :mul].include? operation operands = [operands[0], operands[1], operands[1]] elsif [:sub, :div].include? operation operands = [operands[1], operands[0], operands[1]] end newList << Instruction.new(node.codeOrigin, "rv_f#{operation.to_s}.#{precision.to_s}", operands) end def emitBitwiseOperation(newList, node, operation, precision) riscv64ValidateOperands(node.operands, [FPRegisterID, FPRegisterID]) raise "Invalid operation" unless [:and, :or].include? operation case precision when :f suffix = "w" when :d suffix = "d" else raise "Invalid precision #{precision}" end tmp1 = Tmp.new(node.codeOrigin, :gpr) tmp2 = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_fmv.x.#{suffix}", [node.operands[0], tmp1]) newList << Instruction.new(node.codeOrigin, "rv_fmv.x.#{suffix}", [node.operands[1], tmp2]) newList << Instruction.new(node.codeOrigin, "rv_#{operation.to_s}", [tmp1, tmp2, tmp2]) newList << Instruction.new(node.codeOrigin, "rv_fmv.#{suffix}.x", [tmp2, node.operands[1]]) end def emitRoundingOperation(newList, node, operation, precision) riscv64ValidateOperands(node.operands, [FPRegisterID, FPRegisterID]) rm = RISCV64RoundingMode.new(operation) case precision when :f intSuffix = "w" fpSuffix = "s" when :d intSuffix = "l" fpSuffix = "d" else raise "Invalid precision" end tmp = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_fcvt.#{intSuffix}.#{fpSuffix}", [node.operands[0], tmp, rm]) newList << Instruction.new(node.codeOrigin, "rv_fcvt.#{fpSuffix}.#{intSuffix}", [tmp, node.operands[1], rm]) end def emitConversionOperation(newList, node, sourceType, destinationType, signedness, roundingMode) def registerType(type) case type when :i, :p, :q RegisterID when :f, :d FPRegisterID else raise "Invalid register type #{type}" end end def fpSuffix(type) case type when :f "s" when :d "d" end end def intSuffix(type, signedness) case type when :i signedness == :s ? "w" : "wu" when :q signedness == :s ? "l" : "lu" end end riscv64ValidateOperands(node.operands, [registerType(sourceType), registerType(destinationType)]) case riscv64OperandTypes(node.operands) when [RegisterID, FPRegisterID] raise "Invalid rounding mode" unless roundingMode == :none fcvtOpcode = "rv_fcvt.#{fpSuffix(destinationType)}.#{intSuffix(sourceType, signedness)}" when [FPRegisterID, RegisterID] fcvtOpcode = "rv_fcvt.#{intSuffix(destinationType, signedness)}.#{fpSuffix(sourceType)}" when [FPRegisterID, FPRegisterID] raise "Invalid rounding mode" unless roundingMode == :none fcvtOpcode = "rv_fcvt.#{fpSuffix(destinationType)}.#{fpSuffix(sourceType)}" else riscv64RaiseMismatchedOperands(node.operands) end operands = [node.operands[0], node.operands[1]] if roundingMode != :none operands += [RISCV64RoundingMode.new(roundingMode)] end newList << Instruction.new(node.codeOrigin, fcvtOpcode, operands) end newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when /^load(f|d)$/ emitLoadOperation(newList, node, $1.to_sym) when /^store(f|d)$/ emitStoreOperation(newList, node, $1.to_sym) when /^move(d)$/ emitMoveOperation(newList, node, $1.to_sym) when /^f(i|p|q|f|d)2(i|p|q|f|d)$/ emitCopyOperation(newList, node, $1.to_sym, $2.to_sym) when /^(add|sub|mul|div|sqrt|abs|neg)(f|d)$/ emitComputationalOperation(newList, node, $1.to_sym, $2.to_sym) when /^(and|or)(f|d)$/ emitBitwiseOperation(newList, node, $1.to_sym, $2.to_sym) when /^(floor|ceil|round|truncate)(f|d)$/ emitRoundingOperation(newList, node, $1.to_sym, $2.to_sym) when /^truncate(f|d)2(i|q)(s?)$/ emitConversionOperation(newList, node, $1.to_sym, $2.to_sym, $3.to_sym, :truncate) when /^c(i|q|f|d)2(f|d)(s?)$/ emitConversionOperation(newList, node, $1.to_sym, $2.to_sym, $3.to_sym, :none) else newList << node end else newList << node end } newList end def riscv64LowerFPCompare(list) def emitCompare(newList, node, precision, compareOp, lhs, rhs) case precision when :f precisionSuffix = "s" when :d precisionSuffix = "d" else raise "Invalid precision #{precision}" end newList << Instruction.new(node.codeOrigin, "rv_#{compareOp}.#{precisionSuffix}", [lhs, rhs, node.operands[2]]) end def emit(newList, node, precision, condition) riscv64ValidateOperands(node.operands, [FPRegisterID, FPRegisterID, RegisterID]) operands = node.operands case condition when :eq emitCompare(newList, node, precision, "feq", operands[0], operands[1]) when :neq emitCompare(newList, node, precision, "feq", operands[0], operands[1]) newList << Instruction.new(node.codeOrigin, "rv_xori", [operands[2], Immediate.new(node.codeOrigin, 1), operands[2]]) when :gt emitCompare(newList, node, precision, "flt", operands[1], operands[0]) when :gteq emitCompare(newList, node, precision, "fle", operands[1], operands[0]) when :lt emitCompare(newList, node, precision, "flt", operands[0], operands[1]) when :lteq emitCompare(newList, node, precision, "fle", operands[0], operands[1]) else raise "Invalid condition #{condition}" end end newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when /^c(f|d)(eq|neq|gt|gteq|lt|lteq)$/ emit(newList, node, $1.to_sym, $2.to_sym) else newList << node end else newList << node end } newList end def riscv64LowerFPBranch(list) def precisionSuffix(precision) case precision when :f "s" when :d "d" else raise "Invalid precision" end end def emitBranchForUnordered(newList, node, precision) tmp1 = Tmp.new(node.codeOrigin, :gpr) tmp2 = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_fclass.#{precisionSuffix(precision)}", [node.operands[0], tmp1]) newList << Instruction.new(node.codeOrigin, "rv_fclass.#{precisionSuffix(precision)}", [node.operands[1], tmp2]) newList << Instruction.new(node.codeOrigin, "rv_or", [tmp1, tmp2, tmp2]) newList << Instruction.new(node.codeOrigin, "rv_andi", [tmp2, Immediate.new(node.codeOrigin, 0x300), tmp2]) newList << Instruction.new(node.codeOrigin, "rv_bnez", [tmp2, node.operands[2]]) end def emitBranchForTest(newList, node, precision, testOpcode, lhs, rhs, branchOpcode) tmp = Tmp.new(node.codeOrigin, :gpr) newList << Instruction.new(node.codeOrigin, "rv_#{testOpcode}.#{precisionSuffix(precision)}", [lhs, rhs, tmp]) newList << Instruction.new(node.codeOrigin, "rv_#{branchOpcode}", [tmp, node.operands[2]]) end def emit(newList, node, precision, condition) riscv64ValidateOperands(node.operands, [FPRegisterID, FPRegisterID, LocalLabelReference]) operands = node.operands if [:equn, :nequn, :gtun, :gtequn, :ltun, :ltequn].include? condition emitBranchForUnordered(newList, node, precision) end case condition when :eq, :equn emitBranchForTest(newList, node, precision, "feq", operands[0], operands[1], "bnez") when :neq, :nequn emitBranchForTest(newList, node, precision, "feq", operands[0], operands[1], "beqz") when :gt, :gtun emitBranchForTest(newList, node, precision, "flt", operands[1], operands[0], "bnez") when :gteq, :gtequn emitBranchForTest(newList, node, precision, "fle", operands[1], operands[0], "bnez") when :lt, :ltun emitBranchForTest(newList, node, precision, "flt", operands[0], operands[1], "bnez") when :lteq, :ltequn emitBranchForTest(newList, node, precision, "fle", operands[0], operands[1], "bnez") else raise "Invalid condition" end end newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when /^b(f|d)(eq|neq|gt|gteq|lt|lteq|equn|nequn|gtun|gtequn|ltun|ltequn)$/ emit(newList, node, $1.to_sym, $2.to_sym) else newList << node end else newList << node end } newList end def riscv64GenerateWASMPlaceholders(list) newList = [] list.each { | node | if node.is_a? Instruction case node.opcode when "lrotatei", "lrotateq", "rrotatei", "rrotateq", "tzcnti", "tzcntq", "lzcnti", "lzcntq", "cfnequn", "cdnequn", "loadlinkacqb", "loadlinkacqh", "loadlinkacqi", "loadlinkacqq", "storecondrelb", "storecondrelh", "storecondreli", "storecondrelq" newList << Instruction.new(node.codeOrigin, "rv_ebreak", [], "WebAssembly placeholder for opcode #{node.opcode}") else newList << node end else newList << node end } newList end class Sequence def getModifiedListRISCV64 result = @list result = riscLowerMalformedAddresses(result) { | node, address | if address.is_a? Address !address.riscv64RequiresLoad else false end } result = riscv64LowerMisplacedAddresses(result) result = riscLowerMisplacedAddresses(result) result = riscv64LowerAddressLoads(result) result = riscLowerMisplacedImmediates(result, ["storeb", "storeh", "storei", "storep", "storeq"]) result = riscLowerMalformedImmediates(result, -0x800..0x7ff, -0x800..0x7ff) result = riscv64LowerImmediateSubtraction(result) result = riscv64LowerOperation(result) result = riscv64LowerTest(result) result = riscv64LowerCompare(result) result = riscv64LowerBranch(result) result = riscv64LowerFPOperation(result) result = riscv64LowerFPCompare(result) result = riscv64LowerFPBranch(result) result = riscv64GenerateWASMPlaceholders(result) result = assignRegistersToTemporaries(result, :gpr, RISCV64_EXTRA_GPRS) result = assignRegistersToTemporaries(result, :fpr, RISCV64_EXTRA_FPRS) return result end end class Instruction def rvop(opcode) opcode[/^rv_(.+)/, 1] end def lowerRISCV64 case opcode when /^rv_(jr|jalr)$/ riscv64ValidateOperands(operands, [RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[0].riscv64Operand}" when /^rv_(call|tail)$/ riscv64ValidateOperands(operands, [LabelReference], [LocalLabelReference]) $asm.puts "#{rvop(opcode)} #{operands[0].asmLabel}" when /^rv_(la|lla)$/ riscv64ValidateOperands(operands, [LabelReference, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].asmLabel}" when "rv_mv" riscv64ValidateOperands(operands, [RegisterID, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" when "rv_li" riscv64ValidateOperands(operands, [Immediate, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand(:any_immediate)}" when /^rv_l(b|bu|h|hu|w|wu|d)$/ riscv64ValidateOperands(operands, [Address, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" when /^rv_s(b|h|w|d)$/ riscv64ValidateOperands(operands, [RegisterID, Address]) $asm.puts "#{rvop(opcode)} #{operands[0].riscv64Operand}, #{operands[1].riscv64Operand}" when /^rv_(add(w?)|sub(w?)|and|or|xor|s(ll|rl|ra)(w?)|mul(w?)|div(u?)(w?)|rem(u?)(w?))$/ riscv64ValidateOperands(operands, [RegisterID, RegisterID, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[2].riscv64Operand}, #{operands[0].riscv64Operand}, #{operands[1].riscv64Operand}" when /^rv_addi(w?)$/, /^rv_(and|or|xor)i$/ riscv64ValidateOperands(operands, [RegisterID, Immediate, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[2].riscv64Operand}, #{operands[0].riscv64Operand}, #{operands[1].riscv64Operand}" when /^rv_(sll|srl|sra)i(w?)$/ riscv64ValidateOperands(operands, [RegisterID, Immediate, RegisterID]) validationType = $2 == "w" ? :rv32_shift_immediate : :rv64_shift_immediate raise "Invalid shit-amount immediate" unless riscv64ValidateImmediate(validationType, operands[1].value) $asm.puts "#{rvop(opcode)} #{operands[2].riscv64Operand}, #{operands[0].riscv64Operand}, #{operands[1].riscv64Operand}" when /^rv_neg(w?)$/, "rv_not", "rv_sext.w" riscv64ValidateOperands(operands, [RegisterID, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" when /^rv_(slt|sltu)$/ riscv64ValidateOperands(operands, [RegisterID, RegisterID, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[2].riscv64Operand}, #{operands[0].riscv64Operand}, #{operands[1].riscv64Operand}" when /^rv_(seqz|snez|sltz|sgtz)$/ riscv64ValidateOperands(operands, [RegisterID, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" when /^rv_b(eq|ne|gt|ge|gtu|geu|lt|le|ltu|leu)$/ riscv64ValidateOperands(operands, [RegisterID, RegisterID, LocalLabelReference]) $asm.puts "#{rvop(opcode)} #{operands[0].riscv64Operand}, #{operands[1].riscv64Operand}, #{operands[2].asmLabel}" when /^rv_b(eqz|nez|ltz|gtz)$/ riscv64ValidateOperands(operands, [RegisterID, LocalLabelReference]) $asm.puts "#{rvop(opcode)} #{operands[0].riscv64Operand}, #{operands[1].asmLabel}" when "rv_nop", "rv_ret", "rv_ebreak" $asm.puts "#{rvop(opcode)}" when "rv_fence" riscv64ValidateOperands(operands, [RISCV64MemoryOrdering, RISCV64MemoryOrdering]) $asm.puts "#{rvop(opcode)} #{operands[0].riscv64MemoryOrdering}, #{operands[1].riscv64MemoryOrdering}" when /^rv_fl(w|d)$/ riscv64ValidateOperands(operands, [Address, FPRegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" when /^rv_fs(w|d)$/ riscv64ValidateOperands(operands, [FPRegisterID, Address]) $asm.puts "#{rvop(opcode)} #{operands[0].riscv64Operand}, #{operands[1].riscv64Operand}" when /^rv_fmv\.(s|d)$/ riscv64ValidateOperands(operands, [FPRegisterID, FPRegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" when /^rv_fmv\.(x|w|d)\.(x|w|d)$/ riscv64ValidateOperands(operands, [RegisterID, FPRegisterID], [FPRegisterID, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" when /^rv_f(add|sub|mul|div)\.(s|d)$/ riscv64ValidateOperands(operands, [FPRegisterID, FPRegisterID, FPRegisterID]) $asm.puts "#{rvop(opcode)} #{operands[2].riscv64Operand}, #{operands[0].riscv64Operand}, #{operands[1].riscv64Operand}" when /^rv_f(sqrt|abs|neg)\.(s|d)$/ riscv64ValidateOperands(operands, [FPRegisterID, FPRegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" when /^rv_f(eq|lt|le)\.(s|d)$/ riscv64ValidateOperands(operands, [FPRegisterID, FPRegisterID, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[2].riscv64Operand}, #{operands[0].riscv64Operand}, #{operands[1].riscv64Operand}" when /^rv_fcvt\.(w|wu|l|lu|s|d)\.(w|wu|l|lu|s|d)$/ riscv64ValidateOperands(operands, [RegisterID, FPRegisterID], [FPRegisterID, RegisterID], [FPRegisterID, FPRegisterID], [RegisterID, FPRegisterID, RISCV64RoundingMode], [FPRegisterID, RegisterID, RISCV64RoundingMode]) if operands.size == 3 riscv64RaiseMismatchedOperands(operands) unless operands[2].is_a? RISCV64RoundingMode $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}, #{operands[2].riscv64RoundingMode}" else $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" end when /^rv_fclass\.(s|d)$/ riscv64ValidateOperands(operands, [FPRegisterID, RegisterID]) $asm.puts "#{rvop(opcode)} #{operands[1].riscv64Operand}, #{operands[0].riscv64Operand}" else lowerDefault end end end