From 4987282bbbc8916561134b01a7776d8421985335 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Wed, 3 Aug 2016 15:58:41 -0700 Subject: [PATCH] Add an Encoding meta-language class. Start adding some RISC-V encodings too as a way of testing the ergonomics. --- meta/cretonne/__init__.py | 50 ++++++++++++++++++++++++++++++++++ meta/target/riscv/__init__.py | 10 +++---- meta/target/riscv/defs.py | 14 ++++++++++ meta/target/riscv/encodings.py | 21 ++++++++++++++ meta/target/riscv/recipes.py | 44 ++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 meta/target/riscv/defs.py create mode 100644 meta/target/riscv/encodings.py create mode 100644 meta/target/riscv/recipes.py diff --git a/meta/cretonne/__init__.py b/meta/cretonne/__init__.py index 4ee491f806..483fb1f8e3 100644 --- a/meta/cretonne/__init__.py +++ b/meta/cretonne/__init__.py @@ -671,6 +671,16 @@ class CPUMode(object): def __init__(self, name, target): self.name = name self.target = target + self.encodings = [] + + def enc(self, *args, **kwargs): + """ + Add a new encoding to this CPU mode. + + Arguments are the `Encoding constructor arguments, except for the first + `CPUMode argument which is implied. + """ + self.encodings.append(Encoding(self, *args, **kwargs)) class EncRecipe(object): @@ -689,6 +699,46 @@ class EncRecipe(object): self.name = name self.format = format + +class Encoding(object): + """ + Encoding for a concrete instruction. + + An `Encoding` object ties an instruction opcode with concrete type + variables together with and encoding recipe and encoding bits. + + :param cpumode: The CPU mode where the encoding is active. + :param inst: The :py:class:`Instruction` being encoded. + :param typevars: Concete types for `inst`'s type variables, if any. + :param recipe: The :py:class:`EncRecipe` to use. + :param encbits: Additional encoding bits to be interpreted by `recipe`. + """ + + def __init__(self, cpumode, inst, typevars, recipe, encbits): + assert isinstance(cpumode, CPUMode) + assert isinstance(inst, Instruction) + assert isinstance(recipe, EncRecipe) + self.cpumode = cpumode + assert inst.format == recipe.format, ( + "Format {} must match recipe: {}".format( + inst.format, recipe.format)) + self.inst = inst + self.typevars = self._to_type_tuple(typevars) + self.recipe = recipe + self.encbits = encbits + + @staticmethod + def _to_type_tuple(x): + # Allow a single ValueType instance instead of the awkward singleton + # tuple syntax. + if isinstance(x, ValueType): + x = (x,) + else: + x = tuple(x) + for ty in x: + assert isinstance(ty, ValueType) + return x + # Import the fixed instruction formats now so they can be added to the # registry. importlib.import_module('cretonne.formats') diff --git a/meta/target/riscv/__init__.py b/meta/target/riscv/__init__.py index 918602c78e..48d916f089 100644 --- a/meta/target/riscv/__init__.py +++ b/meta/target/riscv/__init__.py @@ -25,11 +25,9 @@ RV32G / RV64G """ -from cretonne import Target, CPUMode -import cretonne.base +import defs +import encodings -target = Target('riscv', [cretonne.base.instructions]) +# Re-export the primary target definition. +target = defs.target -# CPU modes for 32-bit and 64-bit operation. -RV32 = CPUMode('RV32', target) -RV64 = CPUMode('RV64', target) diff --git a/meta/target/riscv/defs.py b/meta/target/riscv/defs.py new file mode 100644 index 0000000000..aab9279360 --- /dev/null +++ b/meta/target/riscv/defs.py @@ -0,0 +1,14 @@ +""" +RISC-V definitions. + +Commonly used definitions. +""" + +from cretonne import Target, CPUMode +import cretonne.base + +target = Target('riscv', [cretonne.base.instructions]) + +# CPU modes for 32-bit and 64-bit operation. +RV32 = CPUMode('RV32', target) +RV64 = CPUMode('RV64', target) diff --git a/meta/target/riscv/encodings.py b/meta/target/riscv/encodings.py new file mode 100644 index 0000000000..0177cb57f9 --- /dev/null +++ b/meta/target/riscv/encodings.py @@ -0,0 +1,21 @@ +""" +RISC-V Encodings. +""" +from cretonne import base +from cretonne.types import i32, i64 +from defs import RV32, RV64 +from recipes import OP, R + +# Basic arithmetic binary instructions are encoded in an R-type instruction. +for inst, f3, f7 in [ + (base.iadd, 0b000, 0b0000000), + (base.isub, 0b000, 0b0100000), + (base.ishl, 0b001, 0b0000000), + (base.ushr, 0b101, 0b0000000), + (base.sshr, 0b101, 0b0100000), + (base.bxor, 0b100, 0b0000000), + (base.bor, 0b110, 0b0000000), + (base.band, 0b111, 0b0000000) + ]: + RV32.enc(inst, i32, R, OP(f3, f7)) + RV64.enc(inst, i64, R, OP(f3, f7)) diff --git a/meta/target/riscv/recipes.py b/meta/target/riscv/recipes.py new file mode 100644 index 0000000000..ed96836cd4 --- /dev/null +++ b/meta/target/riscv/recipes.py @@ -0,0 +1,44 @@ +""" +RISC-V Encoding recipes. + +The encoding recipes defined here more or less correspond to the RISC-V native +instruction formats described in the reference: + + The RISC-V Instruction Set Manual + Volume I: User-Level ISA + Version 2.1 +""" +from cretonne import EncRecipe +from cretonne.formats import Binary + +# 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 +# opcode. +# +# Encbits for the 32-bit recipes are opcode[6:2] | (funct3 << 5) | ... +# The functions below encode the encbits. + +def LOAD(funct3): + assert funct3 <= 0b111 + return 0b00000 | (funct3 << 5) + +def STORE(funct3): + assert funct3 <= 0b111 + return 0b01000 | (funct3 << 5) + +def BRANCH(funct3): + assert funct3 <= 0b111 + return 0b11000 | (funct3 << 5) + +def OPIMM(funct3): + assert funct3 <= 0b111 + return 0b00100 | (funct3 << 5) + +def OP(funct3, funct7): + assert funct3 <= 0b111 + assert funct7 <= 0b1111111 + return 0b01100 | (funct3 << 5) | (funct7 << 8) + +# R-type 32-bit instructions: These are mostly binary arithmetic instructions. +# The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8) +R = EncRecipe('R', Binary)