Add RISC-V arithmetic w/immediate operand encodings.
Add new instruction predicates to support the 'I' encoding recipe: IsSignedInt, IsUnsignedInt used to test that an immediate operand is in the allowed range.
This commit is contained in:
@@ -725,6 +725,12 @@ class FormatField(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{}.{}'.format(self.format.name, self.name)
|
return '{}.{}'.format(self.format.name, self.name)
|
||||||
|
|
||||||
|
def rust_name(self):
|
||||||
|
if self.format.boxed_storage:
|
||||||
|
return 'data.' + self.name
|
||||||
|
else:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class Instruction(object):
|
class Instruction(object):
|
||||||
"""
|
"""
|
||||||
@@ -983,9 +989,13 @@ class EncRecipe(object):
|
|||||||
:py:class:`InstructionFormat`.
|
:py:class:`InstructionFormat`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, format):
|
def __init__(self, name, format, instp=None, isap=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.format = format
|
self.format = format
|
||||||
|
self.instp = instp
|
||||||
|
self.isap = isap
|
||||||
|
if instp:
|
||||||
|
assert instp.predicate_context() == format
|
||||||
|
|
||||||
|
|
||||||
class Encoding(object):
|
class Encoding(object):
|
||||||
|
|||||||
@@ -125,3 +125,79 @@ class Not(Predicate):
|
|||||||
|
|
||||||
def rust_predicate(self, prec):
|
def rust_predicate(self, prec):
|
||||||
return '!' + self.parts[0].rust_predicate(Not.precedence)
|
return '!' + self.parts[0].rust_predicate(Not.precedence)
|
||||||
|
|
||||||
|
|
||||||
|
class FieldPredicate(object):
|
||||||
|
"""
|
||||||
|
An instruction predicate that performs a test on a single `FormatField`.
|
||||||
|
|
||||||
|
:param field: The `FormatField` to be tested.
|
||||||
|
:param method: Boolean predicate method to call.
|
||||||
|
:param args: Arguments for the predicate method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, field, method, args):
|
||||||
|
self.field = field
|
||||||
|
self.method = method
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def predicate_context(self):
|
||||||
|
"""
|
||||||
|
This predicate can be evaluated in the context of an instruction
|
||||||
|
format.
|
||||||
|
"""
|
||||||
|
return self.field.format
|
||||||
|
|
||||||
|
def rust_predicate(self, prec):
|
||||||
|
"""
|
||||||
|
Return a string of Rust code that evaluates this predicate.
|
||||||
|
"""
|
||||||
|
return '{}.{}({})'.format(
|
||||||
|
self.field.rust_name(),
|
||||||
|
self.method,
|
||||||
|
', '.join(self.args))
|
||||||
|
|
||||||
|
|
||||||
|
class IsSignedInt(FieldPredicate):
|
||||||
|
"""
|
||||||
|
Instruction predicate that checks if an immediate instruction format field
|
||||||
|
is representable as an n-bit two's complement integer.
|
||||||
|
|
||||||
|
:param field: `FormatField` to be checked.
|
||||||
|
:param width: Number of bits in the allowed range.
|
||||||
|
:param scale: Number of low bits that must be 0.
|
||||||
|
|
||||||
|
The predicate is true if the field is in the range:
|
||||||
|
`-2^(width-1) -- 2^(width-1)-1`
|
||||||
|
and a multiple of `2^scale`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, field, width, scale=0):
|
||||||
|
super(IsSignedInt, self).__init__(
|
||||||
|
field, 'is_signed_int', (width, scale))
|
||||||
|
self.width = width
|
||||||
|
self.scale = scale
|
||||||
|
assert width >= 0 and width <= 64
|
||||||
|
assert scale >= 0 and scale < width
|
||||||
|
|
||||||
|
|
||||||
|
class IsUnsignedInt(FieldPredicate):
|
||||||
|
"""
|
||||||
|
Instruction predicate that checks if an immediate instruction format field
|
||||||
|
is representable as an n-bit unsigned complement integer.
|
||||||
|
|
||||||
|
:param field: `FormatField` to be checked.
|
||||||
|
:param width: Number of bits in the allowed range.
|
||||||
|
:param scale: Number of low bits that must be 0.
|
||||||
|
|
||||||
|
The predicate is true if the field is in the range:
|
||||||
|
`0 -- 2^width - 1` and a multiple of `2^scale`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, field, width, scale=0):
|
||||||
|
super(IsUnsignedInt, self).__init__(
|
||||||
|
field, 'is_unsigned_int', (width, scale))
|
||||||
|
self.width = width
|
||||||
|
self.scale = scale
|
||||||
|
assert width >= 0 and width <= 64
|
||||||
|
assert scale >= 0 and scale < width
|
||||||
|
|||||||
@@ -4,20 +4,28 @@ RISC-V Encodings.
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from cretonne import base
|
from cretonne import base
|
||||||
from .defs import RV32, RV64
|
from .defs import RV32, RV64
|
||||||
from .recipes import OPIMM, OPIMM32, OP, OP32, R, Rshamt
|
from .recipes import OPIMM, OPIMM32, OP, OP32, R, Rshamt, I
|
||||||
|
|
||||||
# Basic arithmetic binary instructions are encoded in an R-type instruction.
|
# Basic arithmetic binary instructions are encoded in an R-type instruction.
|
||||||
for inst, f3, f7 in [
|
for inst, inst_imm, f3, f7 in [
|
||||||
(base.iadd, 0b000, 0b0000000),
|
(base.iadd, base.iadd_imm, 0b000, 0b0000000),
|
||||||
(base.isub, 0b000, 0b0100000),
|
(base.isub, None, 0b000, 0b0100000),
|
||||||
(base.bxor, 0b100, 0b0000000),
|
(base.bxor, base.bxor_imm, 0b100, 0b0000000),
|
||||||
(base.bor, 0b110, 0b0000000),
|
(base.bor, base.bor_imm, 0b110, 0b0000000),
|
||||||
(base.band, 0b111, 0b0000000)
|
(base.band, base.band_imm, 0b111, 0b0000000)
|
||||||
]:
|
]:
|
||||||
RV32.enc(inst.i32, R, OP(f3, f7))
|
RV32.enc(inst.i32, R, OP(f3, f7))
|
||||||
RV64.enc(inst.i64, R, OP(f3, f7))
|
RV64.enc(inst.i64, R, OP(f3, f7))
|
||||||
|
|
||||||
# Dynamic shifts have the same masking semantics as the cton base instructions
|
# Immediate versions for add/xor/or/and.
|
||||||
|
if inst_imm:
|
||||||
|
RV32.enc(inst_imm.i32, I, OPIMM(f3))
|
||||||
|
RV64.enc(inst_imm.i64, I, OPIMM(f3))
|
||||||
|
|
||||||
|
# There are no andiw/oriw/xoriw variations.
|
||||||
|
RV64.enc(base.iadd_imm.i32, I, OPIMM32(0b000))
|
||||||
|
|
||||||
|
# Dynamic shifts have the same masking semantics as the cton base instructions.
|
||||||
for inst, inst_imm, f3, f7 in [
|
for inst, inst_imm, f3, f7 in [
|
||||||
(base.ishl, base.ishl_imm, 0b001, 0b0000000),
|
(base.ishl, base.ishl_imm, 0b001, 0b0000000),
|
||||||
(base.ushr, base.ushr_imm, 0b101, 0b0000000),
|
(base.ushr, base.ushr_imm, 0b101, 0b0000000),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ instruction formats described in the reference:
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from cretonne import EncRecipe
|
from cretonne import EncRecipe
|
||||||
from cretonne.formats import Binary, BinaryImm
|
from cretonne.formats import Binary, BinaryImm
|
||||||
|
from cretonne.predicates import IsSignedInt
|
||||||
|
|
||||||
# The low 7 bits of a RISC-V instruction is the base opcode. All 32-bit
|
# The low 7 bits of a RISC-V instruction is the base opcode. All 32-bit
|
||||||
# instructions have 11 as the two low bits, with bits 6:2 determining the base
|
# instructions have 11 as the two low bits, with bits 6:2 determining the base
|
||||||
@@ -63,3 +64,5 @@ R = EncRecipe('R', Binary)
|
|||||||
|
|
||||||
# R-type with an immediate shift amount instead of rs2.
|
# R-type with an immediate shift amount instead of rs2.
|
||||||
Rshamt = EncRecipe('Rshamt', BinaryImm)
|
Rshamt = EncRecipe('Rshamt', BinaryImm)
|
||||||
|
|
||||||
|
I = EncRecipe('I', BinaryImm, instp=IsSignedInt(BinaryImm.imm, 12))
|
||||||
|
|||||||
Reference in New Issue
Block a user