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:
@@ -19,5 +19,20 @@ ebb0:
|
||||
; asm: addl %ecx, %esi
|
||||
[-,%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
|
||||
}
|
||||
|
||||
@@ -12,12 +12,12 @@ register bank.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
composed of two single precision registers.
|
||||
"""
|
||||
@@ -151,6 +151,18 @@ class RegBank(object):
|
||||
# sub-class.
|
||||
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):
|
||||
"""
|
||||
@@ -242,6 +254,15 @@ class RegClass(object):
|
||||
|
||||
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):
|
||||
# type: () -> List[int]
|
||||
"""
|
||||
@@ -298,8 +319,8 @@ class Register(object):
|
||||
Specific registers are used to describe constraints on instructions where
|
||||
some operands must use a fixed register.
|
||||
|
||||
Register objects should be created using the indexing syntax on the
|
||||
register class.
|
||||
Register instances can be created with the constructor, or accessed as
|
||||
attributes on the register class: `GPR.rcx`.
|
||||
"""
|
||||
def __init__(self, rc, unit):
|
||||
# type: (RegClass, int) -> None
|
||||
|
||||
@@ -4,7 +4,14 @@ Intel Encodings.
|
||||
from __future__ import absolute_import
|
||||
from base import instructions as base
|
||||
from .defs import I32
|
||||
from .recipes import Op1rr
|
||||
from .recipes import Op1rr, Op1rc
|
||||
from .recipes import OP
|
||||
|
||||
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))
|
||||
|
||||
@@ -65,3 +65,6 @@ def OP(op, pp=0, mm=0, rrr=0, w=0):
|
||||
|
||||
# XX /r
|
||||
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)
|
||||
|
||||
@@ -13,6 +13,7 @@ fn put_op1<CS: CodeSink + ?Sized>(bits: u16, sink: &mut CS) {
|
||||
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) {
|
||||
let reg = reg 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);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
if let InstructionData::Binary { args, .. } = func.dfg[inst] {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user