Add encodings for Intel dynamic shift instructions.

These instructions have a fixed register constraint; the shift amount is
passed in CL.

Add meta language syntax so a fixed register can be specified as
"GPR.rcx".
This commit is contained in:
Jakob Stoklund Olesen
2017-05-08 20:56:08 -07:00
parent 976b22d816
commit a0085434af
5 changed files with 72 additions and 5 deletions

View File

@@ -19,5 +19,20 @@ ebb0:
; asm: addl %ecx, %esi ; asm: addl %ecx, %esi
[-,%rsi] v11 = iadd v2, v1 ; bin: 01 ce [-,%rsi] v11 = iadd v2, v1 ; bin: 01 ce
; Dynamic shifts take the shift amount in %rcx.
; asm: shll %cl, %esi
[-,%rsi] v12 = ishl v2, v1 ; bin: d3 e6
; asm: shll %cl, %ecx
[-,%rcx] v13 = ishl v1, v1 ; bin: d3 e1
; asm: shrl %cl, %esi
[-,%rsi] v14 = ushr v2, v1 ; bin: d3 ee
; asm: shrl %cl, %ecx
[-,%rcx] v15 = ushr v1, v1 ; bin: d3 e9
; asm: sarl %cl, %esi
[-,%rsi] v16 = sshr v2, v1 ; bin: d3 fe
; asm: sarl %cl, %ecx
[-,%rcx] v17 = sshr v1, v1 ; bin: d3 f9
return return
} }

View File

@@ -12,12 +12,12 @@ register bank.
A register bank consists of a number of *register units* which are the smallest A register bank consists of a number of *register units* which are the smallest
indivisible units of allocation and interference. A register unit doesn't indivisible units of allocation and interference. A register unit doesn't
necesarily correspond to a particular number of bits in a register, it is more necessarily correspond to a particular number of bits in a register, it is more
like a placeholder that can be used to determine of a register is taken or not. like a placeholder that can be used to determine of a register is taken or not.
The register allocator works with *register classes* which can allocate one or The register allocator works with *register classes* which can allocate one or
more register units at a time. A register class allocates more than one more register units at a time. A register class allocates more than one
register unit at a time when its registers are composed of smaller alocatable register unit at a time when its registers are composed of smaller allocatable
units. For example, the ARM double precision floating point registers are units. For example, the ARM double precision floating point registers are
composed of two single precision registers. composed of two single precision registers.
""" """
@@ -151,6 +151,18 @@ class RegBank(object):
# sub-class. # sub-class.
rc2.subclasses.append(rc1) rc2.subclasses.append(rc1)
def unit_by_name(self, name):
# type: (str) -> int
"""
Get a register unit in this bank by name.
"""
if name in self.names:
r = self.names.index(name)
elif name.startswith(self.prefix):
r = int(name[len(self.prefix):])
assert r < self.units, 'Invalid register name: ' + name
return self.first_unit + r
class RegClass(object): class RegClass(object):
""" """
@@ -242,6 +254,15 @@ class RegClass(object):
return RegClass(self.bank, count=c, width=w, start=s) return RegClass(self.bank, count=c, width=w, start=s)
def __getattr__(self, attr):
# type: (str) -> Register
"""
Get a specific register in the class by name.
For example: `GPR.r5`.
"""
return Register(self, self.bank.unit_by_name(attr))
def mask(self): def mask(self):
# type: () -> List[int] # type: () -> List[int]
""" """
@@ -298,8 +319,8 @@ class Register(object):
Specific registers are used to describe constraints on instructions where Specific registers are used to describe constraints on instructions where
some operands must use a fixed register. some operands must use a fixed register.
Register objects should be created using the indexing syntax on the Register instances can be created with the constructor, or accessed as
register class. attributes on the register class: `GPR.rcx`.
""" """
def __init__(self, rc, unit): def __init__(self, rc, unit):
# type: (RegClass, int) -> None # type: (RegClass, int) -> None

View File

@@ -4,7 +4,14 @@ Intel Encodings.
from __future__ import absolute_import from __future__ import absolute_import
from base import instructions as base from base import instructions as base
from .defs import I32 from .defs import I32
from .recipes import Op1rr from .recipes import Op1rr, Op1rc
from .recipes import OP from .recipes import OP
I32.enc(base.iadd.i32, Op1rr, OP(0x01)) I32.enc(base.iadd.i32, Op1rr, OP(0x01))
# 32-bit shifts and rotates.
# Note that the dynamic shift amount is only masked by 5 or 6 bits; the 8-bit
# and 16-bit shifts would need explicit masking.
I32.enc(base.ishl.i32.i32, Op1rc, OP(0xd3, rrr=4))
I32.enc(base.ushr.i32.i32, Op1rc, OP(0xd3, rrr=5))
I32.enc(base.sshr.i32.i32, Op1rc, OP(0xd3, rrr=7))

View File

@@ -65,3 +65,6 @@ def OP(op, pp=0, mm=0, rrr=0, w=0):
# XX /r # XX /r
Op1rr = EncRecipe('Op1rr', Binary, size=2, ins=(GPR, GPR), outs=0) Op1rr = EncRecipe('Op1rr', Binary, size=2, ins=(GPR, GPR), outs=0)
# XX /n with one arg in %rcx, for shifts.
Op1rc = EncRecipe('Op1rc', Binary, size=2, ins=(GPR, GPR.rcx), outs=0)

View File

@@ -13,6 +13,7 @@ fn put_op1<CS: CodeSink + ?Sized>(bits: u16, sink: &mut CS) {
sink.put1(bits as u8); sink.put1(bits as u8);
} }
/// Emit a ModR/M byte for reg-reg operands.
fn modrm_rr<CS: CodeSink + ?Sized>(rm: RegUnit, reg: RegUnit, sink: &mut CS) { fn modrm_rr<CS: CodeSink + ?Sized>(rm: RegUnit, reg: RegUnit, sink: &mut CS) {
let reg = reg as u8 & 7; let reg = reg as u8 & 7;
let rm = rm as u8 & 7; let rm = rm as u8 & 7;
@@ -22,6 +23,16 @@ fn modrm_rr<CS: CodeSink + ?Sized>(rm: RegUnit, reg: RegUnit, sink: &mut CS) {
sink.put1(b); sink.put1(b);
} }
/// Emit a ModR/M byte where the reg bits are part of the opcode.
fn modrm_r_bits<CS: CodeSink + ?Sized>(rm: RegUnit, bits: u16, sink: &mut CS) {
let reg = (bits >> 12) as u8 & 7;
let rm = rm as u8 & 7;
let mut b = 0b11000000;
b |= reg << 3;
b |= rm;
sink.put1(b);
}
fn recipe_op1rr<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) { fn recipe_op1rr<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Binary { args, .. } = func.dfg[inst] { if let InstructionData::Binary { args, .. } = func.dfg[inst] {
put_op1(func.encodings[inst].bits(), sink); put_op1(func.encodings[inst].bits(), sink);
@@ -32,3 +43,13 @@ fn recipe_op1rr<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut C
panic!("Expected Binary format: {:?}", func.dfg[inst]); panic!("Expected Binary format: {:?}", func.dfg[inst]);
} }
} }
fn recipe_op1rc<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Binary { args, .. } = func.dfg[inst] {
let bits = func.encodings[inst].bits();
put_op1(bits, sink);
modrm_r_bits(func.locations[args[0]].unwrap_reg(), bits, sink);
} else {
panic!("Expected Binary format: {:?}", func.dfg[inst]);
}
}