diff --git a/cranelift/filetests/isa/intel/binary32.cton b/cranelift/filetests/isa/intel/binary32.cton index c8440440b9..d609a0c030 100644 --- a/cranelift/filetests/isa/intel/binary32.cton +++ b/cranelift/filetests/isa/intel/binary32.cton @@ -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 } diff --git a/lib/cretonne/meta/cdsl/registers.py b/lib/cretonne/meta/cdsl/registers.py index b21d2348a6..1f6bfa682f 100644 --- a/lib/cretonne/meta/cdsl/registers.py +++ b/lib/cretonne/meta/cdsl/registers.py @@ -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 diff --git a/lib/cretonne/meta/isa/intel/encodings.py b/lib/cretonne/meta/isa/intel/encodings.py index 5d1ffdb4ec..3896532c85 100644 --- a/lib/cretonne/meta/isa/intel/encodings.py +++ b/lib/cretonne/meta/isa/intel/encodings.py @@ -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)) diff --git a/lib/cretonne/meta/isa/intel/recipes.py b/lib/cretonne/meta/isa/intel/recipes.py index 9535598917..0ffd7e5c50 100644 --- a/lib/cretonne/meta/isa/intel/recipes.py +++ b/lib/cretonne/meta/isa/intel/recipes.py @@ -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) diff --git a/lib/cretonne/src/isa/intel/binemit.rs b/lib/cretonne/src/isa/intel/binemit.rs index 4b322c6969..0644d583d3 100644 --- a/lib/cretonne/src/isa/intel/binemit.rs +++ b/lib/cretonne/src/isa/intel/binemit.rs @@ -13,6 +13,7 @@ fn put_op1(bits: u16, sink: &mut CS) { sink.put1(bits as u8); } +/// Emit a ModR/M byte for reg-reg operands. fn modrm_rr(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(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(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(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(func: &Function, inst: Inst, sink: &mut C panic!("Expected Binary format: {:?}", func.dfg[inst]); } } + +fn recipe_op1rc(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]); + } +}