blob: 48d57b149fcf9ebf66876d083805b469e0c53e53 [file] [log] [blame]
# 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<number> => GPR, used to operate with 32-bit and 64-bit integer values
# f<number> => 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