From a951fa2d9d2cd9559d8ecb398afb67e5eba62610 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 15 Dec 2017 23:35:18 +0100 Subject: [PATCH] ARM64 wip; --- filetests/isa/arm64/binary.cton | 20 ++++ lib/cretonne/meta/gen_encoding.py | 4 +- lib/cretonne/meta/isa/arm64/__init__.py | 2 +- lib/cretonne/meta/isa/arm64/defs.py | 3 - lib/cretonne/meta/isa/arm64/encodings.py | 139 +++++++++++++++++++++++ lib/cretonne/meta/isa/arm64/recipes.py | 36 ++++++ lib/cretonne/src/isa/arm64/binemit.rs | 28 ++++- lib/cretonne/src/isa/arm64/enc_tables.rs | 2 + 8 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 filetests/isa/arm64/binary.cton create mode 100644 lib/cretonne/meta/isa/arm64/encodings.py create mode 100644 lib/cretonne/meta/isa/arm64/recipes.py diff --git a/filetests/isa/arm64/binary.cton b/filetests/isa/arm64/binary.cton new file mode 100644 index 000000000..865073c3a --- /dev/null +++ b/filetests/isa/arm64/binary.cton @@ -0,0 +1,20 @@ +; Binary emission. +test binemit +isa arm64 + +; The binary encodings can be verified with the command: +; +; sed -ne 's/^ *; asm: *//p' filetests/isa/arm64/binary.cton | llvm-mc -show-encoding -triple=arm64 +; + +function %RV32I(i32 link [%x1]) -> i32 link [%x1] { + fn0 = function %foo() + sig0 = () + +ebb0(v9999: i32): + ; asm: mov w2, #1 + [-,%x2] v1 = iconst.i32 1 ; bin: 52800022 + ; asm: mov x3, #2 + [-,%x3] v2 = iconst.i64 2 ; bin: d2800043 + return v1 +} diff --git a/lib/cretonne/meta/gen_encoding.py b/lib/cretonne/meta/gen_encoding.py index 46fe22a91..8e439c370 100644 --- a/lib/cretonne/meta/gen_encoding.py +++ b/lib/cretonne/meta/gen_encoding.py @@ -2,7 +2,7 @@ Generate sources for instruction encoding. The tables and functions generated here support the `TargetISA::encode()` -function which determines if a given instruction is legal, and if so, it's +function which determines if a given instruction is legal, and if so, its `Encoding` data which consists of a *recipe* and some *encoding* bits. The `encode` function doesn't actually generate the binary machine bits. Each @@ -44,7 +44,7 @@ satisfied. Otherwise, stop with the default legalization code. 3. Stop with legalization code. 4. Predicate + skip count. Test predicate and skip N entries if it is false. -4. Predicate + stop. Test predicate and stop with the default legalization code +5. Predicate + stop. Test predicate and stop with the default legalization code if it is false. The instruction predicate is also used to distinguish between polymorphic diff --git a/lib/cretonne/meta/isa/arm64/__init__.py b/lib/cretonne/meta/isa/arm64/__init__.py index 3dd69feb4..615f0da2d 100644 --- a/lib/cretonne/meta/isa/arm64/__init__.py +++ b/lib/cretonne/meta/isa/arm64/__init__.py @@ -7,7 +7,7 @@ from __future__ import absolute_import from . import defs -from . import settings, registers # noqa +from . import encodings, settings, registers # noqa # Re-export the primary target ISA definition. ISA = defs.ISA.finish() diff --git a/lib/cretonne/meta/isa/arm64/defs.py b/lib/cretonne/meta/isa/arm64/defs.py index b1ed79b5d..c94c488ab 100644 --- a/lib/cretonne/meta/isa/arm64/defs.py +++ b/lib/cretonne/meta/isa/arm64/defs.py @@ -10,6 +10,3 @@ ISA = TargetISA('arm64', [base.instructions.GROUP]) A64 = CPUMode('A64', ISA) - -# TODO: Refine these -A64.legalize_type(narrow) diff --git a/lib/cretonne/meta/isa/arm64/encodings.py b/lib/cretonne/meta/isa/arm64/encodings.py new file mode 100644 index 000000000..8b8f2a7bf --- /dev/null +++ b/lib/cretonne/meta/isa/arm64/encodings.py @@ -0,0 +1,139 @@ +""" +ARM64 Encodings. +""" +from __future__ import absolute_import +from base import instructions as base +from base.immediates import intcc + +from .defs import A64 +from .recipes import MOVZ +from .recipes import Move16 + +from cdsl.ast import Var +from base.legalize import narrow, expand + +# TODO what does this control? +A64.legalize_monomorphic(expand) +A64.legalize_type( + default=narrow, + i32=expand, + i64=expand, + f32=expand, + f64=expand) + +# Dummies for instruction predicates. +x = Var('x') +y = Var('y') +dest = Var('dest') +args = Var('args') + +# Small constants (16 bits). +A64.enc(base.iconst.i32, Move16, MOVZ(is64bits=0b0)) +A64.enc(base.iconst.i64, Move16, MOVZ(is64bits=0b1)) + +# Integer constants with the low 12 bits clear are materialized by lui. +# A64.enc(base.iconst.i32, U, LUI()) +# A64.enc(base.iconst.i64, U, LUI()) + +# # Basic arithmetic binary instructions are encoded in an R-type instruction. +# for inst, inst_imm, f3, f7 in [ + # (base.iadd, base.iadd_imm, 0b000, 0b0000000), + # (base.isub, None, 0b000, 0b0100000), + # (base.bxor, base.bxor_imm, 0b100, 0b0000000), + # (base.bor, base.bor_imm, 0b110, 0b0000000), + # (base.band, base.band_imm, 0b111, 0b0000000) + # ]: + # A64.enc(inst.i64, R, OP(f3, f7)) + + # # Immediate versions for add/xor/or/and. + # if inst_imm: + # A64.enc(inst_imm.i64, Ii, OPIMM(f3)) + +# # 32-bit ops in A64. +# A64.enc(base.iadd.i32, R, OP32(0b000, 0b0000000)) +# A64.enc(base.isub.i32, R, OP32(0b000, 0b0100000)) +# # There are no andiw/oriw/xoriw variations. +# A64.enc(base.iadd_imm.i32, Ii, OPIMM32(0b000)) + +# # Dynamic shifts have the same masking semantics as the cton base instructions. +# for inst, inst_imm, f3, f7 in [ + # (base.ishl, base.ishl_imm, 0b001, 0b0000000), + # (base.ushr, base.ushr_imm, 0b101, 0b0000000), + # (base.sshr, base.sshr_imm, 0b101, 0b0100000), + # ]: + + # A64.enc(inst.i64.i64, R, OP(f3, f7)) + # A64.enc(inst.i32.i32, R, OP32(f3, f7)) + # # Allow i32 shift amounts in 64-bit shifts. + # A64.enc(inst.i64.i32, R, OP(f3, f7)) + # A64.enc(inst.i32.i64, R, OP32(f3, f7)) + + # # Immediate shifts. + + # A64.enc(inst_imm.i64, Rshamt, OPIMM(f3, f7)) + # A64.enc(inst_imm.i32, Rshamt, OPIMM32(f3, f7)) + +# # Signed and unsigned integer 'less than'. There are no 'w' variants for +# # comparing 32-bit numbers in A64. + +# A64.enc(base.icmp.i64(intcc.slt, x, y), Ricmp, OP(0b010, 0b0000000)) + +# A64.enc(base.icmp.i64(intcc.ult, x, y), Ricmp, OP(0b011, 0b0000000)) + + +# A64.enc(base.icmp_imm.i64(intcc.slt, x, y), Iicmp, OPIMM(0b010)) + +# A64.enc(base.icmp_imm.i64(intcc.ult, x, y), Iicmp, OPIMM(0b011)) + +# # Control flow. + +# # Unconditional branches. + +# A64.enc(base.jump, UJ, JAL()) + +# A64.enc(base.call, UJcall, JAL()) + +# # Conditional branches. +# for cond, f3 in [ + # (intcc.eq, 0b000), + # (intcc.ne, 0b001), + # (intcc.slt, 0b100), + # (intcc.sge, 0b101), + # (intcc.ult, 0b110), + # (intcc.uge, 0b111) + # ]: + + # A64.enc(base.br_icmp.i64(cond, x, y, dest, args), SB, BRANCH(f3)) + +# for inst, f3 in [ + # (base.brz, 0b000), + # (base.brnz, 0b001) + # ]: + + # A64.enc(inst.i64, SBzero, BRANCH(f3)) + + # A64.enc(inst.b1, SBzero, BRANCH(f3)) + +# # Returns are a special case of JALR using %x1 to hold the return address. +# # The return address is provided by a special-purpose `link` return value that +# # is added by legalize_signature(). + +# A64.enc(base.x_return, Iret, JALR()) + +# A64.enc(base.call_indirect.i64, Icall, JALR()) + +# # Spill and fill. + +# A64.enc(base.spill.i32, GPsp, STORE(0b010)) +# A64.enc(base.spill.i64, GPsp, STORE(0b011)) + +# A64.enc(base.fill.i32, GPfi, LOAD(0b010)) +# A64.enc(base.fill.i64, GPfi, LOAD(0b011)) + +# # Register copies. + +# A64.enc(base.copy.i64, Icopy, OPIMM(0b000)) +# A64.enc(base.copy.i32, Icopy, OPIMM32(0b000)) + +# A64.enc(base.regmove.i64, Irmov, OPIMM(0b000)) +# A64.enc(base.regmove.i32, Irmov, OPIMM32(0b000)) diff --git a/lib/cretonne/meta/isa/arm64/recipes.py b/lib/cretonne/meta/isa/arm64/recipes.py new file mode 100644 index 000000000..195bf8fa1 --- /dev/null +++ b/lib/cretonne/meta/isa/arm64/recipes.py @@ -0,0 +1,36 @@ +""" +ARM64 Encoding recipes. + +The encoding recipes defined here correspond to the ARM64 native instruction +formats described in the reference. +""" +from __future__ import absolute_import +from cdsl.isa import EncRecipe +from cdsl.predicates import IsSignedInt +from cdsl.registers import Stack +from base.formats import Binary, BinaryImm, MultiAry, IntCompare, IntCompareImm +from base.formats import Unary, UnaryImm, BranchIcmp, Branch, Jump +from base.formats import Call, IndirectCall, RegMove +from .registers import GPR, FPR, FLAG + +def dataProcessing(op0): + assert op0 <= 0b111 + return (0b100 << 26) | (op0 << 23) + +def moveWide(is64bits, opc): + assert is64bits <= 0b1 + assert opc <= 0b11 + return ((is64bits << 31) | (opc << 29) | dataProcessing(0b101)) >> 23 + +def MOVN(is64bits): + return moveWide(is64bits, 0b00) +def MOVZ(is64bits): + return moveWide(is64bits, 0b10) +def MOVK(is64bits): + return moveWide(is64bits, 0b11) + +Move16 = EncRecipe( + 'Move16', UnaryImm, size=4, ins=(), outs=GPR, + # TODO the predicate could be more precise: has <= 16 adjacent non-zero bits + instp=IsSignedInt(UnaryImm.imm, 16), + emit='put_move(bits, imm.into(), out_reg0, sink);') diff --git a/lib/cretonne/src/isa/arm64/binemit.rs b/lib/cretonne/src/isa/arm64/binemit.rs index a4b79cda5..d70d05430 100644 --- a/lib/cretonne/src/isa/arm64/binemit.rs +++ b/lib/cretonne/src/isa/arm64/binemit.rs @@ -1,7 +1,33 @@ //! Emitting binary ARM64 machine code. use binemit::{CodeSink, bad_encoding}; -use ir::{Function, Inst}; +use ir::{Function, Inst, InstructionData}; +use isa::{RegUnit, StackRef, StackBaseMask}; use regalloc::RegDiversions; include!(concat!(env!("OUT_DIR"), "/binemit-arm64.rs")); + +pub static RELOC_NAMES: [&'static str; 1] = ["Call"]; + +/// Move instruction. +/// +/// 31 22 20 4 +/// bits hw imm16 Rd +/// 23 21 5 0 +fn put_move(bits: u16, imm: i64, rd: RegUnit, sink: &mut CS) { + let rd = u32::from(rd) & 0x1f; + + let mut i: u32 = rd; + assert!(imm <= 0xffff); + i |= (imm as u32) << 5; + + // TODO shifts are not handled here... yet. + let shift = 0; + assert!(shift <= 48 && shift % 16 == 0); + let hw = shift / 16; + + i |= hw << 21; + i |= (bits as u32) << 23; + + sink.put4(i); +} diff --git a/lib/cretonne/src/isa/arm64/enc_tables.rs b/lib/cretonne/src/isa/arm64/enc_tables.rs index 6007450cb..38c82c2a5 100644 --- a/lib/cretonne/src/isa/arm64/enc_tables.rs +++ b/lib/cretonne/src/isa/arm64/enc_tables.rs @@ -5,6 +5,8 @@ use isa; use isa::constraints::*; use isa::enc_tables::*; use isa::encoding::RecipeSizing; +use super::registers::*; +use predicates; include!(concat!(env!("OUT_DIR"), "/encoding-arm64.rs")); include!(concat!(env!("OUT_DIR"), "/legalize-arm64.rs"));