diff options
Diffstat (limited to 'compiler/dex/quick/mips')
-rw-r--r-- | compiler/dex/quick/mips/README.mips | 57 | ||||
-rw-r--r-- | compiler/dex/quick/mips/assemble_mips.cc | 716 | ||||
-rw-r--r-- | compiler/dex/quick/mips/call_mips.cc | 392 | ||||
-rw-r--r-- | compiler/dex/quick/mips/codegen_mips.h | 183 | ||||
-rw-r--r-- | compiler/dex/quick/mips/fp_mips.cc | 248 | ||||
-rw-r--r-- | compiler/dex/quick/mips/int_mips.cc | 659 | ||||
-rw-r--r-- | compiler/dex/quick/mips/mips_lir.h | 432 | ||||
-rw-r--r-- | compiler/dex/quick/mips/target_mips.cc | 610 | ||||
-rw-r--r-- | compiler/dex/quick/mips/utility_mips.cc | 700 |
9 files changed, 3997 insertions, 0 deletions
diff --git a/compiler/dex/quick/mips/README.mips b/compiler/dex/quick/mips/README.mips new file mode 100644 index 0000000000..061c157256 --- /dev/null +++ b/compiler/dex/quick/mips/README.mips @@ -0,0 +1,57 @@ + Notes on the Mips target (3/4/2012) + ----------------------------------- + +Testing + +The initial implementation of Mips support in the compiler is untested on +actual hardware, and as such should be expected to have many bugs. However, +the vast majority of code for Mips support is either shared with other +tested targets, or was taken from the functional Mips JIT compiler. The +expectation is that when it is first tried out on actual hardware lots of +small bugs will be flushed out, but it should not take long to get it +solidly running. The following areas are considered most likely to have +problems that need to be addressed: + + o Endianness. Focus was on little-endian support, and if a big-endian + target is desired, you should pay particular attention to the + code generation for switch tables, fill array data, 64-bit + data handling and the register usage conventions. + + o The memory model. Verify that oatGenMemoryBarrier() generates the + appropriate flavor of sync. + +Register promotion + +The resource masks in the LIR structure are 64-bits wide, which is enough +room to fully describe def/use info for Arm and x86 instructions. However, +the larger number of MIPS core and float registers render this too small. +Currently, the workaround for this limitation is to avoid using floating +point registers 16-31. These are the callee-save registers, which therefore +means that no floating point promotion is allowed. Among the solution are: + o Expand the def/use mask (which, unfortunately, is a significant change) + o The Arm target uses 52 of the 64 bits, so we could support float + registers 16-27 without much effort. + o We could likely assign the 4 non-register bits (kDalvikReg, kLiteral, + kHeapRef & kMustNotAlias) to positions occuped by MIPS registers that + don't need def/use bits because they are never modified by code + subject to scheduling: r_K0, r_K1, r_SP, r_ZERO, r_S1 (rSELF). + +Branch delay slots + +Little to no attempt was made to fill branch delay slots. Branch +instructions in the encoding map are given a length of 8 bytes to include +an implicit NOP. It should not be too difficult to provide a slot-filling +pass following successful assembly, but thought should be given to the +design. Branches are currently treated as scheduling barriers. One +simple solution would be to copy the instruction at branch targets to the +slot and adjust the displacement. However, given that code expansion is +already a problem it would be preferable to use a more sophisticated +scheduling solution. + +Code expansion + +Code expansion for the MIPS target is significantly higher than we see +for Arm and x86. It might make sense to replace the inline code generation +for some of the more verbose Dalik byte codes with subroutine calls to +shared helper functions. + diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc new file mode 100644 index 0000000000..2482aa4fbb --- /dev/null +++ b/compiler/dex/quick/mips/assemble_mips.cc @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "codegen_mips.h" +#include "dex/quick/mir_to_lir-inl.h" +#include "mips_lir.h" + +namespace art { + +#define MAX_ASSEMBLER_RETRIES 50 + +/* + * opcode: MipsOpCode enum + * skeleton: pre-designated bit-pattern for this opcode + * k0: key to applying ds/de + * ds: dest start bit position + * de: dest end bit position + * k1: key to applying s1s/s1e + * s1s: src1 start bit position + * s1e: src1 end bit position + * k2: key to applying s2s/s2e + * s2s: src2 start bit position + * s2e: src2 end bit position + * operands: number of operands (for sanity check purposes) + * name: mnemonic name + * fmt: for pretty-printing + */ +#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \ + k3, k3s, k3e, flags, name, fmt, size) \ + {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \ + {k3, k3s, k3e}}, opcode, flags, name, fmt, size} + +/* Instruction dump string format keys: !pf, where "!" is the start + * of the key, "p" is which numeric operand to use and "f" is the + * print format. + * + * [p]ositions: + * 0 -> operands[0] (dest) + * 1 -> operands[1] (src1) + * 2 -> operands[2] (src2) + * 3 -> operands[3] (extra) + * + * [f]ormats: + * h -> 4-digit hex + * d -> decimal + * E -> decimal*4 + * F -> decimal*2 + * c -> branch condition (beq, bne, etc.) + * t -> pc-relative target + * T -> pc-region target + * u -> 1st half of bl[x] target + * v -> 2nd half ob bl[x] target + * R -> register list + * s -> single precision floating point register + * S -> double precision floating point register + * m -> Thumb2 modified immediate + * n -> complimented Thumb2 modified immediate + * M -> Thumb2 16-bit zero-extended immediate + * b -> 4-digit binary + * N -> append a NOP + * + * [!] escape. To insert "!", use "!!" + */ +/* NOTE: must be kept in sync with enum MipsOpcode from LIR.h */ +/* + * TUNING: We're currently punting on the branch delay slots. All branch + * instructions in this map are given a size of 8, which during assembly + * is expanded to include a nop. This scheme should be replaced with + * an assembler pass to fill those slots when possible. + */ +const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { + ENCODING_MAP(kMips32BitData, 0x00000000, + kFmtBitBlt, 31, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP, + "data", "0x!0h(!0d)", 4), + ENCODING_MAP(kMipsAddiu, 0x24000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "addiu", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMipsAddu, 0x00000021, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "addu", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsAnd, 0x00000024, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "and", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsAndi, 0x30000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "andi", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMipsB, 0x10000000, + kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, + "b", "!0t!0N", 8), + ENCODING_MAP(kMipsBal, 0x04110000, + kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | + NEEDS_FIXUP, "bal", "!0t!0N", 8), + ENCODING_MAP(kMipsBeq, 0x10000000, + kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01 | + NEEDS_FIXUP, "beq", "!0r,!1r,!2t!0N", 8), + ENCODING_MAP(kMipsBeqz, 0x10000000, /* same as beq above with t = $zero */ + kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | + NEEDS_FIXUP, "beqz", "!0r,!1t!0N", 8), + ENCODING_MAP(kMipsBgez, 0x04010000, + kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | + NEEDS_FIXUP, "bgez", "!0r,!1t!0N", 8), + ENCODING_MAP(kMipsBgtz, 0x1C000000, + kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | + NEEDS_FIXUP, "bgtz", "!0r,!1t!0N", 8), + ENCODING_MAP(kMipsBlez, 0x18000000, + kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | + NEEDS_FIXUP, "blez", "!0r,!1t!0N", 8), + ENCODING_MAP(kMipsBltz, 0x04000000, + kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | + NEEDS_FIXUP, "bltz", "!0r,!1t!0N", 8), + ENCODING_MAP(kMipsBnez, 0x14000000, /* same as bne below with t = $zero */ + kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | + NEEDS_FIXUP, "bnez", "!0r,!1t!0N", 8), + ENCODING_MAP(kMipsBne, 0x14000000, + kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01 | + NEEDS_FIXUP, "bne", "!0r,!1r,!2t!0N", 8), + ENCODING_MAP(kMipsDiv, 0x0000001a, + kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtBitBlt, 25, 21, + kFmtBitBlt, 20, 16, IS_QUAD_OP | REG_DEF01 | REG_USE23, + "div", "!2r,!3r", 4), +#if __mips_isa_rev>=2 + ENCODING_MAP(kMipsExt, 0x7c000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 10, 6, + kFmtBitBlt, 15, 11, IS_QUAD_OP | REG_DEF0 | REG_USE1, + "ext", "!0r,!1r,!2d,!3D", 4), +#endif + ENCODING_MAP(kMipsJal, 0x0c000000, + kFmtBitBlt, 25, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR, + "jal", "!0T(!0E)!0N", 8), + ENCODING_MAP(kMipsJalr, 0x00000009, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF0_USE1, + "jalr", "!0r,!1r!0N", 8), + ENCODING_MAP(kMipsJr, 0x00000008, + kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | + NEEDS_FIXUP, "jr", "!0r!0N", 8), + ENCODING_MAP(kMipsLahi, 0x3C000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, + "lahi/lui", "!0r,0x!1h(!1d)", 4), + ENCODING_MAP(kMipsLalo, 0x34000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "lalo/ori", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMipsLui, 0x3C000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, + "lui", "!0r,0x!1h(!1d)", 4), + ENCODING_MAP(kMipsLb, 0x80000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, + "lb", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMipsLbu, 0x90000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, + "lbu", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMipsLh, 0x84000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, + "lh", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMipsLhu, 0x94000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, + "lhu", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMipsLw, 0x8C000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, + "lw", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMipsMfhi, 0x00000010, + kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "mfhi", "!0r", 4), + ENCODING_MAP(kMipsMflo, 0x00000012, + kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "mflo", "!0r", 4), + ENCODING_MAP(kMipsMove, 0x00000025, /* or using zero reg */ + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "move", "!0r,!1r", 4), + ENCODING_MAP(kMipsMovz, 0x0000000a, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "movz", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsMul, 0x70000002, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "mul", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsNop, 0x00000000, + kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, NO_OPERAND, + "nop", ";", 4), + ENCODING_MAP(kMipsNor, 0x00000027, /* used for "not" too */ + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "nor", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsOr, 0x00000025, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "or", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsOri, 0x34000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "ori", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMipsPref, 0xCC000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE2, + "pref", "!0d,!1d(!2r)", 4), + ENCODING_MAP(kMipsSb, 0xA0000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, + "sb", "!0r,!1d(!2r)", 4), +#if __mips_isa_rev>=2 + ENCODING_MAP(kMipsSeb, 0x7c000420, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "seb", "!0r,!1r", 4), + ENCODING_MAP(kMipsSeh, 0x7c000620, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "seh", "!0r,!1r", 4), +#endif + ENCODING_MAP(kMipsSh, 0xA4000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, + "sh", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMipsSll, 0x00000000, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "sll", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMipsSllv, 0x00000004, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "sllv", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsSlt, 0x0000002a, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "slt", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsSlti, 0x28000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "slti", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMipsSltu, 0x0000002b, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "sltu", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsSra, 0x00000003, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "sra", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMipsSrav, 0x00000007, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "srav", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsSrl, 0x00000002, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "srl", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMipsSrlv, 0x00000006, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "srlv", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsSubu, 0x00000023, /* used for "neg" too */ + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "subu", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsSw, 0xAC000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, + "sw", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMipsXor, 0x00000026, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "xor", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsXori, 0x38000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "xori", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMipsFadds, 0x46000000, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "add.s", "!0s,!1s,!2s", 4), + ENCODING_MAP(kMipsFsubs, 0x46000001, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "sub.s", "!0s,!1s,!2s", 4), + ENCODING_MAP(kMipsFmuls, 0x46000002, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "mul.s", "!0s,!1s,!2s", 4), + ENCODING_MAP(kMipsFdivs, 0x46000003, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "div.s", "!0s,!1s,!2s", 4), + ENCODING_MAP(kMipsFaddd, 0x46200000, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "add.d", "!0S,!1S,!2S", 4), + ENCODING_MAP(kMipsFsubd, 0x46200001, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "sub.d", "!0S,!1S,!2S", 4), + ENCODING_MAP(kMipsFmuld, 0x46200002, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "mul.d", "!0S,!1S,!2S", 4), + ENCODING_MAP(kMipsFdivd, 0x46200003, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "div.d", "!0S,!1S,!2S", 4), + ENCODING_MAP(kMipsFcvtsd, 0x46200020, + kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.s.d", "!0s,!1S", 4), + ENCODING_MAP(kMipsFcvtsw, 0x46800020, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.s.w", "!0s,!1s", 4), + ENCODING_MAP(kMipsFcvtds, 0x46000021, + kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.d.s", "!0S,!1s", 4), + ENCODING_MAP(kMipsFcvtdw, 0x46800021, + kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.d.w", "!0S,!1s", 4), + ENCODING_MAP(kMipsFcvtws, 0x46000024, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.w.s", "!0s,!1s", 4), + ENCODING_MAP(kMipsFcvtwd, 0x46200024, + kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.w.d", "!0s,!1S", 4), + ENCODING_MAP(kMipsFmovs, 0x46000006, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "mov.s", "!0s,!1s", 4), + ENCODING_MAP(kMipsFmovd, 0x46200006, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "mov.d", "!0S,!1S", 4), + ENCODING_MAP(kMipsFlwc1, 0xC4000000, + kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, + "lwc1", "!0s,!1d(!2r)", 4), + ENCODING_MAP(kMipsFldc1, 0xD4000000, + kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, + "ldc1", "!0S,!1d(!2r)", 4), + ENCODING_MAP(kMipsFswc1, 0xE4000000, + kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, + "swc1", "!0s,!1d(!2r)", 4), + ENCODING_MAP(kMipsFsdc1, 0xF4000000, + kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, + "sdc1", "!0S,!1d(!2r)", 4), + ENCODING_MAP(kMipsMfc1, 0x44000000, + kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "mfc1", "!0r,!1s", 4), + ENCODING_MAP(kMipsMtc1, 0x44800000, + kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1, + "mtc1", "!0r,!1s", 4), + ENCODING_MAP(kMipsDelta, 0x27e00000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, 15, 0, + kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0 | REG_USE_LR | + NEEDS_FIXUP, "addiu", "!0r,ra,0x!1h(!1d)", 4), + ENCODING_MAP(kMipsDeltaHi, 0x3C000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0 | NEEDS_FIXUP, + "lui", "!0r,0x!1h(!1d)", 4), + ENCODING_MAP(kMipsDeltaLo, 0x34000000, + kFmtBlt5_2, 16, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0_USE0 | NEEDS_FIXUP, + "ori", "!0r,!0r,0x!1h(!1d)", 4), + ENCODING_MAP(kMipsCurrPC, 0x04110001, + kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH | REG_DEF_LR, + "addiu", "ra,pc,8", 4), + ENCODING_MAP(kMipsSync, 0x0000000f, + kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP, + "sync", ";", 4), + ENCODING_MAP(kMipsUndefined, 0x64000000, + kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, NO_OPERAND, + "undefined", "", 4), +}; + + +/* + * Convert a short-form branch to long form. Hopefully, this won't happen + * very often because the PIC sequence is especially unfortunate. + * + * Orig conditional branch + * ----------------------- + * beq rs,rt,target + * + * Long conditional branch + * ----------------------- + * bne rs,rt,hop + * bal .+8 ; r_RA <- anchor + * lui r_AT, ((target-anchor) >> 16) + * anchor: + * ori r_AT, r_AT, ((target-anchor) & 0xffff) + * addu r_AT, r_AT, r_RA + * jr r_AT + * hop: + * + * Orig unconditional branch + * ------------------------- + * b target + * + * Long unconditional branch + * ----------------------- + * bal .+8 ; r_RA <- anchor + * lui r_AT, ((target-anchor) >> 16) + * anchor: + * ori r_AT, r_AT, ((target-anchor) & 0xffff) + * addu r_AT, r_AT, r_RA + * jr r_AT + * + * + * NOTE: An out-of-range bal isn't supported because it should + * never happen with the current PIC model. + */ +void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir) +{ + // For conditional branches we'll need to reverse the sense + bool unconditional = false; + int opcode = lir->opcode; + int dalvik_offset = lir->dalvik_offset; + switch (opcode) { + case kMipsBal: + LOG(FATAL) << "long branch and link unsupported"; + case kMipsB: + unconditional = true; + break; + case kMipsBeq: opcode = kMipsBne; break; + case kMipsBne: opcode = kMipsBeq; break; + case kMipsBeqz: opcode = kMipsBnez; break; + case kMipsBgez: opcode = kMipsBltz; break; + case kMipsBgtz: opcode = kMipsBlez; break; + case kMipsBlez: opcode = kMipsBgtz; break; + case kMipsBltz: opcode = kMipsBgez; break; + case kMipsBnez: opcode = kMipsBeqz; break; + default: + LOG(FATAL) << "Unexpected branch kind " << opcode; + } + LIR* hop_target = NULL; + if (!unconditional) { + hop_target = RawLIR(dalvik_offset, kPseudoTargetLabel); + LIR* hop_branch = RawLIR(dalvik_offset, opcode, lir->operands[0], + lir->operands[1], 0, 0, 0, hop_target); + InsertLIRBefore(lir, hop_branch); + } + LIR* curr_pc = RawLIR(dalvik_offset, kMipsCurrPC); + InsertLIRBefore(lir, curr_pc); + LIR* anchor = RawLIR(dalvik_offset, kPseudoTargetLabel); + LIR* delta_hi = RawLIR(dalvik_offset, kMipsDeltaHi, r_AT, 0, + reinterpret_cast<uintptr_t>(anchor), 0, 0, lir->target); + InsertLIRBefore(lir, delta_hi); + InsertLIRBefore(lir, anchor); + LIR* delta_lo = RawLIR(dalvik_offset, kMipsDeltaLo, r_AT, 0, + reinterpret_cast<uintptr_t>(anchor), 0, 0, lir->target); + InsertLIRBefore(lir, delta_lo); + LIR* addu = RawLIR(dalvik_offset, kMipsAddu, r_AT, r_AT, r_RA); + InsertLIRBefore(lir, addu); + LIR* jr = RawLIR(dalvik_offset, kMipsJr, r_AT); + InsertLIRBefore(lir, jr); + if (!unconditional) { + InsertLIRBefore(lir, hop_target); + } + lir->flags.is_nop = true; +} + +/* + * Assemble the LIR into binary instruction format. Note that we may + * discover that pc-relative displacements may not fit the selected + * instruction. In those cases we will try to substitute a new code + * sequence or request that the trace be shortened and retried. + */ +AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) +{ + LIR *lir; + AssemblerStatus res = kSuccess; // Assume success + + for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { + if (lir->opcode < 0) { + continue; + } + + + if (lir->flags.is_nop) { + continue; + } + + if (lir->flags.pcRelFixup) { + if (lir->opcode == kMipsDelta) { + /* + * The "Delta" pseudo-ops load the difference between + * two pc-relative locations into a the target register + * found in operands[0]. The delta is determined by + * (label2 - label1), where label1 is a standard + * kPseudoTargetLabel and is stored in operands[2]. + * If operands[3] is null, then label2 is a kPseudoTargetLabel + * and is found in lir->target. If operands[3] is non-NULL, + * then it is a Switch/Data table. + */ + int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset; + SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]); + int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; + int delta = offset2 - offset1; + if ((delta & 0xffff) == delta && ((delta & 0x8000) == 0)) { + // Fits + lir->operands[1] = delta; + } else { + // Doesn't fit - must expand to kMipsDelta[Hi|Lo] pair + LIR *new_delta_hi = + RawLIR(lir->dalvik_offset, kMipsDeltaHi, + lir->operands[0], 0, lir->operands[2], + lir->operands[3], 0, lir->target); + InsertLIRBefore(lir, new_delta_hi); + LIR *new_delta_lo = + RawLIR(lir->dalvik_offset, kMipsDeltaLo, + lir->operands[0], 0, lir->operands[2], + lir->operands[3], 0, lir->target); + InsertLIRBefore(lir, new_delta_lo); + LIR *new_addu = + RawLIR(lir->dalvik_offset, kMipsAddu, + lir->operands[0], lir->operands[0], r_RA); + InsertLIRBefore(lir, new_addu); + lir->flags.is_nop = true; + res = kRetryAll; + } + } else if (lir->opcode == kMipsDeltaLo) { + int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset; + SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]); + int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; + int delta = offset2 - offset1; + lir->operands[1] = delta & 0xffff; + } else if (lir->opcode == kMipsDeltaHi) { + int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset; + SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]); + int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; + int delta = offset2 - offset1; + lir->operands[1] = (delta >> 16) & 0xffff; + } else if (lir->opcode == kMipsB || lir->opcode == kMipsBal) { + LIR *target_lir = lir->target; + uintptr_t pc = lir->offset + 4; + uintptr_t target = target_lir->offset; + int delta = target - pc; + if (delta & 0x3) { + LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; + } + if (delta > 131068 || delta < -131069) { + res = kRetryAll; + ConvertShortToLongBranch(lir); + } else { + lir->operands[0] = delta >> 2; + } + } else if (lir->opcode >= kMipsBeqz && lir->opcode <= kMipsBnez) { + LIR *target_lir = lir->target; + uintptr_t pc = lir->offset + 4; + uintptr_t target = target_lir->offset; + int delta = target - pc; + if (delta & 0x3) { + LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; + } + if (delta > 131068 || delta < -131069) { + res = kRetryAll; + ConvertShortToLongBranch(lir); + } else { + lir->operands[1] = delta >> 2; + } + } else if (lir->opcode == kMipsBeq || lir->opcode == kMipsBne) { + LIR *target_lir = lir->target; + uintptr_t pc = lir->offset + 4; + uintptr_t target = target_lir->offset; + int delta = target - pc; + if (delta & 0x3) { + LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; + } + if (delta > 131068 || delta < -131069) { + res = kRetryAll; + ConvertShortToLongBranch(lir); + } else { + lir->operands[2] = delta >> 2; + } + } else if (lir->opcode == kMipsJal) { + uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3; + uintptr_t target = lir->operands[0]; + /* ensure PC-region branch can be used */ + DCHECK_EQ((cur_pc & 0xF0000000), (target & 0xF0000000)); + if (target & 0x3) { + LOG(FATAL) << "Jump target not multiple of 4: " << target; + } + lir->operands[0] = target >> 2; + } else if (lir->opcode == kMipsLahi) { /* ld address hi (via lui) */ + LIR *target_lir = lir->target; + uintptr_t target = start_addr + target_lir->offset; + lir->operands[1] = target >> 16; + } else if (lir->opcode == kMipsLalo) { /* ld address lo (via ori) */ + LIR *target_lir = lir->target; + uintptr_t target = start_addr + target_lir->offset; + lir->operands[2] = lir->operands[2] + target; + } + } + + /* + * If one of the pc-relative instructions expanded we'll have + * to make another pass. Don't bother to fully assemble the + * instruction. + */ + if (res != kSuccess) { + continue; + } + const MipsEncodingMap *encoder = &EncodingMap[lir->opcode]; + uint32_t bits = encoder->skeleton; + int i; + for (i = 0; i < 4; i++) { + uint32_t operand; + uint32_t value; + operand = lir->operands[i]; + switch (encoder->field_loc[i].kind) { + case kFmtUnused: + break; + case kFmtBitBlt: + if (encoder->field_loc[i].start == 0 && encoder->field_loc[i].end == 31) { + value = operand; + } else { + value = (operand << encoder->field_loc[i].start) & + ((1 << (encoder->field_loc[i].end + 1)) - 1); + } + bits |= value; + break; + case kFmtBlt5_2: + value = (operand & 0x1f); + bits |= (value << encoder->field_loc[i].start); + bits |= (value << encoder->field_loc[i].end); + break; + case kFmtDfp: { + DCHECK(MIPS_DOUBLEREG(operand)); + DCHECK_EQ((operand & 0x1), 0U); + value = ((operand & MIPS_FP_REG_MASK) << encoder->field_loc[i].start) & + ((1 << (encoder->field_loc[i].end + 1)) - 1); + bits |= value; + break; + } + case kFmtSfp: + DCHECK(MIPS_SINGLEREG(operand)); + value = ((operand & MIPS_FP_REG_MASK) << encoder->field_loc[i].start) & + ((1 << (encoder->field_loc[i].end + 1)) - 1); + bits |= value; + break; + default: + LOG(FATAL) << "Bad encoder format: " << encoder->field_loc[i].kind; + } + } + // We only support little-endian MIPS. + code_buffer_.push_back(bits & 0xff); + code_buffer_.push_back((bits >> 8) & 0xff); + code_buffer_.push_back((bits >> 16) & 0xff); + code_buffer_.push_back((bits >> 24) & 0xff); + // TUNING: replace with proper delay slot handling + if (encoder->size == 8) { + const MipsEncodingMap *encoder = &EncodingMap[kMipsNop]; + uint32_t bits = encoder->skeleton; + code_buffer_.push_back(bits & 0xff); + code_buffer_.push_back((bits >> 8) & 0xff); + code_buffer_.push_back((bits >> 16) & 0xff); + code_buffer_.push_back((bits >> 24) & 0xff); + } + } + return res; +} + +int MipsMir2Lir::GetInsnSize(LIR* lir) +{ + return EncodingMap[lir->opcode].size; +} + +} // namespace art diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc new file mode 100644 index 0000000000..eb0302e80f --- /dev/null +++ b/compiler/dex/quick/mips/call_mips.cc @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This file contains codegen for the Mips ISA */ + +#include "codegen_mips.h" +#include "dex/quick/mir_to_lir-inl.h" +#include "mips_lir.h" +#include "oat/runtime/oat_support_entrypoints.h" + +namespace art { + +void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, + SpecialCaseHandler special_case) +{ + // TODO +} + +/* + * The lack of pc-relative loads on Mips presents somewhat of a challenge + * for our PIC switch table strategy. To materialize the current location + * we'll do a dummy JAL and reference our tables using r_RA as the + * base register. Note that r_RA will be used both as the base to + * locate the switch table data and as the reference base for the switch + * target offsets stored in the table. We'll use a special pseudo-instruction + * to represent the jal and trigger the construction of the + * switch table offsets (which will happen after final assembly and all + * labels are fixed). + * + * The test loop will look something like: + * + * ori rEnd, r_ZERO, #table_size ; size in bytes + * jal BaseLabel ; stores "return address" (BaseLabel) in r_RA + * nop ; opportunistically fill + * BaseLabel: + * addiu rBase, r_RA, <table> - <BaseLabel> ; table relative to BaseLabel + addu rEnd, rEnd, rBase ; end of table + * lw r_val, [rSP, v_reg_off] ; Test Value + * loop: + * beq rBase, rEnd, done + * lw r_key, 0(rBase) + * addu rBase, 8 + * bne r_val, r_key, loop + * lw r_disp, -4(rBase) + * addu r_RA, r_disp + * jr r_RA + * done: + * + */ +void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, + RegLocation rl_src) +{ + const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; + if (cu_->verbose) { + DumpSparseSwitchTable(table); + } + // Add the table to the list - we'll process it later + SwitchTable *tab_rec = + static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true, + ArenaAllocator::kAllocData)); + tab_rec->table = table; + tab_rec->vaddr = current_dalvik_offset_; + int elements = table[1]; + tab_rec->targets = + static_cast<LIR**>(arena_->NewMem(elements * sizeof(LIR*), true, ArenaAllocator::kAllocLIR)); + switch_tables_.Insert(tab_rec); + + // The table is composed of 8-byte key/disp pairs + int byte_size = elements * 8; + + int size_hi = byte_size >> 16; + int size_lo = byte_size & 0xffff; + + int rEnd = AllocTemp(); + if (size_hi) { + NewLIR2(kMipsLui, rEnd, size_hi); + } + // Must prevent code motion for the curr pc pair + GenBarrier(); // Scheduling barrier + NewLIR0(kMipsCurrPC); // Really a jal to .+8 + // Now, fill the branch delay slot + if (size_hi) { + NewLIR3(kMipsOri, rEnd, rEnd, size_lo); + } else { + NewLIR3(kMipsOri, rEnd, r_ZERO, size_lo); + } + GenBarrier(); // Scheduling barrier + + // Construct BaseLabel and set up table base register + LIR* base_label = NewLIR0(kPseudoTargetLabel); + // Remember base label so offsets can be computed later + tab_rec->anchor = base_label; + int rBase = AllocTemp(); + NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label), + reinterpret_cast<uintptr_t>(tab_rec)); + OpRegRegReg(kOpAdd, rEnd, rEnd, rBase); + + // Grab switch test value + rl_src = LoadValue(rl_src, kCoreReg); + + // Test loop + int r_key = AllocTemp(); + LIR* loop_label = NewLIR0(kPseudoTargetLabel); + LIR* exit_branch = OpCmpBranch(kCondEq, rBase, rEnd, NULL); + LoadWordDisp(rBase, 0, r_key); + OpRegImm(kOpAdd, rBase, 8); + OpCmpBranch(kCondNe, rl_src.low_reg, r_key, loop_label); + int r_disp = AllocTemp(); + LoadWordDisp(rBase, -4, r_disp); + OpRegRegReg(kOpAdd, r_RA, r_RA, r_disp); + OpReg(kOpBx, r_RA); + + // Loop exit + LIR* exit_label = NewLIR0(kPseudoTargetLabel); + exit_branch->target = exit_label; +} + +/* + * Code pattern will look something like: + * + * lw r_val + * jal BaseLabel ; stores "return address" (BaseLabel) in r_RA + * nop ; opportunistically fill + * [subiu r_val, bias] ; Remove bias if low_val != 0 + * bound check -> done + * lw r_disp, [r_RA, r_val] + * addu r_RA, r_disp + * jr r_RA + * done: + */ +void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, + RegLocation rl_src) +{ + const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; + if (cu_->verbose) { + DumpPackedSwitchTable(table); + } + // Add the table to the list - we'll process it later + SwitchTable *tab_rec = + static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true, + ArenaAllocator::kAllocData)); + tab_rec->table = table; + tab_rec->vaddr = current_dalvik_offset_; + int size = table[1]; + tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true, + ArenaAllocator::kAllocLIR)); + switch_tables_.Insert(tab_rec); + + // Get the switch value + rl_src = LoadValue(rl_src, kCoreReg); + + // Prepare the bias. If too big, handle 1st stage here + int low_key = s4FromSwitchData(&table[2]); + bool large_bias = false; + int r_key; + if (low_key == 0) { + r_key = rl_src.low_reg; + } else if ((low_key & 0xffff) != low_key) { + r_key = AllocTemp(); + LoadConstant(r_key, low_key); + large_bias = true; + } else { + r_key = AllocTemp(); + } + + // Must prevent code motion for the curr pc pair + GenBarrier(); + NewLIR0(kMipsCurrPC); // Really a jal to .+8 + // Now, fill the branch delay slot with bias strip + if (low_key == 0) { + NewLIR0(kMipsNop); + } else { + if (large_bias) { + OpRegRegReg(kOpSub, r_key, rl_src.low_reg, r_key); + } else { + OpRegRegImm(kOpSub, r_key, rl_src.low_reg, low_key); + } + } + GenBarrier(); // Scheduling barrier + + // Construct BaseLabel and set up table base register + LIR* base_label = NewLIR0(kPseudoTargetLabel); + // Remember base label so offsets can be computed later + tab_rec->anchor = base_label; + + // Bounds check - if < 0 or >= size continue following switch + LIR* branch_over = OpCmpImmBranch(kCondHi, r_key, size-1, NULL); + + // Materialize the table base pointer + int rBase = AllocTemp(); + NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label), + reinterpret_cast<uintptr_t>(tab_rec)); + + // Load the displacement from the switch table + int r_disp = AllocTemp(); + LoadBaseIndexed(rBase, r_key, r_disp, 2, kWord); + + // Add to r_AP and go + OpRegRegReg(kOpAdd, r_RA, r_RA, r_disp); + OpReg(kOpBx, r_RA); + + /* branch_over target here */ + LIR* target = NewLIR0(kPseudoTargetLabel); + branch_over->target = target; +} + +/* + * Array data table format: + * ushort ident = 0x0300 magic value + * ushort width width of each element in the table + * uint size number of elements in the table + * ubyte data[size*width] table of data values (may contain a single-byte + * padding at the end) + * + * Total size is 4+(width * size + 1)/2 16-bit code units. + */ +void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) +{ + const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; + // Add the table to the list - we'll process it later + FillArrayData *tab_rec = + reinterpret_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true, + ArenaAllocator::kAllocData)); + tab_rec->table = table; + tab_rec->vaddr = current_dalvik_offset_; + uint16_t width = tab_rec->table[1]; + uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16); + tab_rec->size = (size * width) + 8; + + fill_array_data_.Insert(tab_rec); + + // Making a call - use explicit registers + FlushAllRegs(); /* Everything to home location */ + LockCallTemps(); + LoadValueDirectFixed(rl_src, rMIPS_ARG0); + + // Must prevent code motion for the curr pc pair + GenBarrier(); + NewLIR0(kMipsCurrPC); // Really a jal to .+8 + // Now, fill the branch delay slot with the helper load + int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pHandleFillArrayDataFromCode)); + GenBarrier(); // Scheduling barrier + + // Construct BaseLabel and set up table base register + LIR* base_label = NewLIR0(kPseudoTargetLabel); + + // Materialize a pointer to the fill data image + NewLIR4(kMipsDelta, rMIPS_ARG1, 0, reinterpret_cast<uintptr_t>(base_label), + reinterpret_cast<uintptr_t>(tab_rec)); + + // And go... + ClobberCalleeSave(); + LIR* call_inst = OpReg(kOpBlx, r_tgt); // ( array*, fill_data* ) + MarkSafepointPC(call_inst); +} + +/* + * TODO: implement fast path to short-circuit thin-lock case + */ +void MipsMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) +{ + FlushAllRegs(); + LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj + LockCallTemps(); // Prepare for explicit register usage + GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags); + // Go expensive route - artLockObjectFromCode(self, obj); + int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pLockObjectFromCode)); + ClobberCalleeSave(); + LIR* call_inst = OpReg(kOpBlx, r_tgt); + MarkSafepointPC(call_inst); +} + +/* + * TODO: implement fast path to short-circuit thin-lock case + */ +void MipsMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) +{ + FlushAllRegs(); + LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj + LockCallTemps(); // Prepare for explicit register usage + GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags); + // Go expensive route - UnlockObjectFromCode(obj); + int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pUnlockObjectFromCode)); + ClobberCalleeSave(); + LIR* call_inst = OpReg(kOpBlx, r_tgt); + MarkSafepointPC(call_inst); +} + +void MipsMir2Lir::GenMoveException(RegLocation rl_dest) +{ + int ex_offset = Thread::ExceptionOffset().Int32Value(); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + int reset_reg = AllocTemp(); + LoadWordDisp(rMIPS_SELF, ex_offset, rl_result.low_reg); + LoadConstant(reset_reg, 0); + StoreWordDisp(rMIPS_SELF, ex_offset, reset_reg); + FreeTemp(reset_reg); + StoreValue(rl_dest, rl_result); +} + +/* + * Mark garbage collection card. Skip if the value we're storing is null. + */ +void MipsMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg) +{ + int reg_card_base = AllocTemp(); + int reg_card_no = AllocTemp(); + LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); + LoadWordDisp(rMIPS_SELF, Thread::CardTableOffset().Int32Value(), reg_card_base); + OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); + StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, + kUnsignedByte); + LIR* target = NewLIR0(kPseudoTargetLabel); + branch_over->target = target; + FreeTemp(reg_card_base); + FreeTemp(reg_card_no); +} +void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) +{ + int spill_count = num_core_spills_ + num_fp_spills_; + /* + * On entry, rMIPS_ARG0, rMIPS_ARG1, rMIPS_ARG2 & rMIPS_ARG3 are live. Let the register + * allocation mechanism know so it doesn't try to use any of them when + * expanding the frame or flushing. This leaves the utility + * code with a single temp: r12. This should be enough. + */ + LockTemp(rMIPS_ARG0); + LockTemp(rMIPS_ARG1); + LockTemp(rMIPS_ARG2); + LockTemp(rMIPS_ARG3); + + /* + * We can safely skip the stack overflow check if we're + * a leaf *and* our frame size < fudge factor. + */ + bool skip_overflow_check = (mir_graph_->MethodIsLeaf() && + (static_cast<size_t>(frame_size_) < Thread::kStackOverflowReservedBytes)); + NewLIR0(kPseudoMethodEntry); + int check_reg = AllocTemp(); + int new_sp = AllocTemp(); + if (!skip_overflow_check) { + /* Load stack limit */ + LoadWordDisp(rMIPS_SELF, Thread::StackEndOffset().Int32Value(), check_reg); + } + /* Spill core callee saves */ + SpillCoreRegs(); + /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */ + DCHECK_EQ(num_fp_spills_, 0); + if (!skip_overflow_check) { + OpRegRegImm(kOpSub, new_sp, rMIPS_SP, frame_size_ - (spill_count * 4)); + GenRegRegCheck(kCondCc, new_sp, check_reg, kThrowStackOverflow); + OpRegCopy(rMIPS_SP, new_sp); // Establish stack + } else { + OpRegImm(kOpSub, rMIPS_SP, frame_size_ - (spill_count * 4)); + } + + FlushIns(ArgLocs, rl_method); + + FreeTemp(rMIPS_ARG0); + FreeTemp(rMIPS_ARG1); + FreeTemp(rMIPS_ARG2); + FreeTemp(rMIPS_ARG3); +} + +void MipsMir2Lir::GenExitSequence() +{ + /* + * In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't + * allocated by the register utilities as temps. + */ + LockTemp(rMIPS_RET0); + LockTemp(rMIPS_RET1); + + NewLIR0(kPseudoMethodExit); + UnSpillCoreRegs(); + OpReg(kOpBx, r_RA); +} + +} // namespace art diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h new file mode 100644 index 0000000000..9723b899a9 --- /dev/null +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_DEX_QUICK_CODEGEN_MIPS_CODEGENMIPS_H_ +#define ART_SRC_DEX_QUICK_CODEGEN_MIPS_CODEGENMIPS_H_ + +#include "dex/compiler_internals.h" +#include "mips_lir.h" + +namespace art { + +class MipsMir2Lir : public Mir2Lir { + public: + + MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); + + // Required for target - codegen utilities. + bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src, + RegLocation rl_dest, int lit); + int LoadHelper(int offset); + LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg); + LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi, + int s_reg); + LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size); + LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, + int r_dest, int r_dest_hi, OpSize size, int s_reg); + LIR* LoadConstantNoClobber(int r_dest, int value); + LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value); + LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size); + LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi); + LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size); + LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, + int r_src, int r_src_hi, OpSize size, int s_reg); + void MarkGCCard(int val_reg, int tgt_addr_reg); + + // Required for target - register utilities. + bool IsFpReg(int reg); + bool SameRegType(int reg1, int reg2); + int AllocTypedTemp(bool fp_hint, int reg_class); + int AllocTypedTempPair(bool fp_hint, int reg_class); + int S2d(int low_reg, int high_reg); + int TargetReg(SpecialTargetRegister reg); + RegisterInfo* GetRegInfo(int reg); + RegLocation GetReturnAlt(); + RegLocation GetReturnWideAlt(); + RegLocation LocCReturn(); + RegLocation LocCReturnDouble(); + RegLocation LocCReturnFloat(); + RegLocation LocCReturnWide(); + uint32_t FpRegMask(); + uint64_t GetRegMaskCommon(int reg); + void AdjustSpillMask(); + void ClobberCalleeSave(); + void FlushReg(int reg); + void FlushRegWide(int reg1, int reg2); + void FreeCallTemps(); + void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free); + void LockCallTemps(); + void MarkPreservedSingle(int v_reg, int reg); + void CompilerInitializeRegAlloc(); + + // Required for target - miscellaneous. + AssemblerStatus AssembleInstructions(uintptr_t start_addr); + void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); + void SetupTargetResourceMasks(LIR* lir); + const char* GetTargetInstFmt(int opcode); + const char* GetTargetInstName(int opcode); + std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); + uint64_t GetPCUseDefEncoding(); + uint64_t GetTargetInstFlags(int opcode); + int GetInsnSize(LIR* lir); + bool IsUnconditionalBranch(LIR* lir); + + // Required for target - Dalvik-level generators. + void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2); + void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, + RegLocation rl_src, int scale); + void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, + RegLocation rl_index, RegLocation rl_dest, int scale); + void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, + RegLocation rl_index, RegLocation rl_src, int scale); + void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_shift); + void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2); + void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2); + void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2); + void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); + bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); + bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); + bool GenInlinedSqrt(CallInfo* info); + void GenNegLong(RegLocation rl_dest, RegLocation rl_src); + void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, + ThrowKind kind); + RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); + RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div); + void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenDivZeroCheck(int reg_lo, int reg_hi); + void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); + void GenExitSequence(); + void GenFillArrayData(uint32_t table_offset, RegLocation rl_src); + void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); + void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); + void GenSelect(BasicBlock* bb, MIR* mir); + void GenMemBarrier(MemBarrierKind barrier_kind); + void GenMonitorEnter(int opt_flags, RegLocation rl_src); + void GenMonitorExit(int opt_flags, RegLocation rl_src); + void GenMoveException(RegLocation rl_dest); + void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, + int first_bit, int second_bit); + void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); + void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); + void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + + // Required for target - single operation generators. + LIR* OpUnconditionalBranch(LIR* target); + LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target); + LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target); + LIR* OpCondBranch(ConditionCode cc, LIR* target); + LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target); + LIR* OpFpRegCopy(int r_dest, int r_src); + LIR* OpIT(ConditionCode cond, const char* guide); + LIR* OpMem(OpKind op, int rBase, int disp); + LIR* OpPcRelLoad(int reg, LIR* target); + LIR* OpReg(OpKind op, int r_dest_src); + LIR* OpRegCopy(int r_dest, int r_src); + LIR* OpRegCopyNoInsert(int r_dest, int r_src); + LIR* OpRegImm(OpKind op, int r_dest_src1, int value); + LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); + LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); + LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); + LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); + LIR* OpTestSuspend(LIR* target); + LIR* OpThreadMem(OpKind op, int thread_offset); + LIR* OpVldm(int rBase, int count); + LIR* OpVstm(int rBase, int count); + void OpLea(int rBase, int reg1, int reg2, int scale, int offset); + void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi); + void OpTlsCmp(int offset, int val); + + LIR* LoadBaseDispBody(int rBase, int displacement, int r_dest, int r_dest_hi, OpSize size, + int s_reg); + LIR* StoreBaseDispBody(int rBase, int displacement, int r_src, int r_src_hi, OpSize size); + void SpillCoreRegs(); + void UnSpillCoreRegs(); + static const MipsEncodingMap EncodingMap[kMipsLast]; + bool InexpensiveConstantInt(int32_t value); + bool InexpensiveConstantFloat(int32_t value); + bool InexpensiveConstantLong(int64_t value); + bool InexpensiveConstantDouble(int64_t value); + + private: + void ConvertShortToLongBranch(LIR* lir); + +}; + +} // namespace art + +#endif // ART_SRC_DEX_QUICK_CODEGEN_MIPS_CODEGENMIPS_H_ diff --git a/compiler/dex/quick/mips/fp_mips.cc b/compiler/dex/quick/mips/fp_mips.cc new file mode 100644 index 0000000000..8581d5beb6 --- /dev/null +++ b/compiler/dex/quick/mips/fp_mips.cc @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "codegen_mips.h" +#include "dex/quick/mir_to_lir-inl.h" +#include "mips_lir.h" +#include "oat/runtime/oat_support_entrypoints.h" + +namespace art { + +void MipsMir2Lir::GenArithOpFloat(Instruction::Code opcode, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) +{ + int op = kMipsNop; + RegLocation rl_result; + + /* + * Don't attempt to optimize register usage since these opcodes call out to + * the handlers. + */ + switch (opcode) { + case Instruction::ADD_FLOAT_2ADDR: + case Instruction::ADD_FLOAT: + op = kMipsFadds; + break; + case Instruction::SUB_FLOAT_2ADDR: + case Instruction::SUB_FLOAT: + op = kMipsFsubs; + break; + case Instruction::DIV_FLOAT_2ADDR: + case Instruction::DIV_FLOAT: + op = kMipsFdivs; + break; + case Instruction::MUL_FLOAT_2ADDR: + case Instruction::MUL_FLOAT: + op = kMipsFmuls; + break; + case Instruction::REM_FLOAT_2ADDR: + case Instruction::REM_FLOAT: + FlushAllRegs(); // Send everything to home location + CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmodf), rl_src1, rl_src2, false); + rl_result = GetReturn(true); + StoreValue(rl_dest, rl_result); + return; + case Instruction::NEG_FLOAT: + GenNegFloat(rl_dest, rl_src1); + return; + default: + LOG(FATAL) << "Unexpected opcode: " << opcode; + } + rl_src1 = LoadValue(rl_src1, kFPReg); + rl_src2 = LoadValue(rl_src2, kFPReg); + rl_result = EvalLoc(rl_dest, kFPReg, true); + NewLIR3(op, rl_result.low_reg, rl_src1.low_reg, rl_src2.low_reg); + StoreValue(rl_dest, rl_result); +} + +void MipsMir2Lir::GenArithOpDouble(Instruction::Code opcode, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) +{ + int op = kMipsNop; + RegLocation rl_result; + + switch (opcode) { + case Instruction::ADD_DOUBLE_2ADDR: + case Instruction::ADD_DOUBLE: + op = kMipsFaddd; + break; + case Instruction::SUB_DOUBLE_2ADDR: + case Instruction::SUB_DOUBLE: + op = kMipsFsubd; + break; + case Instruction::DIV_DOUBLE_2ADDR: + case Instruction::DIV_DOUBLE: + op = kMipsFdivd; + break; + case Instruction::MUL_DOUBLE_2ADDR: + case Instruction::MUL_DOUBLE: + op = kMipsFmuld; + break; + case Instruction::REM_DOUBLE_2ADDR: + case Instruction::REM_DOUBLE: + FlushAllRegs(); // Send everything to home location + CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmod), rl_src1, rl_src2, false); + rl_result = GetReturnWide(true); + StoreValueWide(rl_dest, rl_result); + return; + case Instruction::NEG_DOUBLE: + GenNegDouble(rl_dest, rl_src1); + return; + default: + LOG(FATAL) << "Unpexpected opcode: " << opcode; + } + rl_src1 = LoadValueWide(rl_src1, kFPReg); + DCHECK(rl_src1.wide); + rl_src2 = LoadValueWide(rl_src2, kFPReg); + DCHECK(rl_src2.wide); + rl_result = EvalLoc(rl_dest, kFPReg, true); + DCHECK(rl_dest.wide); + DCHECK(rl_result.wide); + NewLIR3(op, S2d(rl_result.low_reg, rl_result.high_reg), S2d(rl_src1.low_reg, rl_src1.high_reg), + S2d(rl_src2.low_reg, rl_src2.high_reg)); + StoreValueWide(rl_dest, rl_result); +} + +void MipsMir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src) +{ + int op = kMipsNop; + int src_reg; + RegLocation rl_result; + switch (opcode) { + case Instruction::INT_TO_FLOAT: + op = kMipsFcvtsw; + break; + case Instruction::DOUBLE_TO_FLOAT: + op = kMipsFcvtsd; + break; + case Instruction::FLOAT_TO_DOUBLE: + op = kMipsFcvtds; + break; + case Instruction::INT_TO_DOUBLE: + op = kMipsFcvtdw; + break; + case Instruction::FLOAT_TO_INT: + GenConversionCall(ENTRYPOINT_OFFSET(pF2iz), rl_dest, rl_src); + return; + case Instruction::DOUBLE_TO_INT: + GenConversionCall(ENTRYPOINT_OFFSET(pD2iz), rl_dest, rl_src); + return; + case Instruction::LONG_TO_DOUBLE: + GenConversionCall(ENTRYPOINT_OFFSET(pL2d), rl_dest, rl_src); + return; + case Instruction::FLOAT_TO_LONG: + GenConversionCall(ENTRYPOINT_OFFSET(pF2l), rl_dest, rl_src); + return; + case Instruction::LONG_TO_FLOAT: + GenConversionCall(ENTRYPOINT_OFFSET(pL2f), rl_dest, rl_src); + return; + case Instruction::DOUBLE_TO_LONG: + GenConversionCall(ENTRYPOINT_OFFSET(pD2l), rl_dest, rl_src); + return; + default: + LOG(FATAL) << "Unexpected opcode: " << opcode; + } + if (rl_src.wide) { + rl_src = LoadValueWide(rl_src, kFPReg); + src_reg = S2d(rl_src.low_reg, rl_src.high_reg); + } else { + rl_src = LoadValue(rl_src, kFPReg); + src_reg = rl_src.low_reg; + } + if (rl_dest.wide) { + rl_result = EvalLoc(rl_dest, kFPReg, true); + NewLIR2(op, S2d(rl_result.low_reg, rl_result.high_reg), src_reg); + StoreValueWide(rl_dest, rl_result); + } else { + rl_result = EvalLoc(rl_dest, kFPReg, true); + NewLIR2(op, rl_result.low_reg, src_reg); + StoreValue(rl_dest, rl_result); + } +} + +void MipsMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) +{ + bool wide = true; + int offset = -1; // Make gcc happy. + + switch (opcode) { + case Instruction::CMPL_FLOAT: + offset = ENTRYPOINT_OFFSET(pCmplFloat); + wide = false; + break; + case Instruction::CMPG_FLOAT: + offset = ENTRYPOINT_OFFSET(pCmpgFloat); + wide = false; + break; + case Instruction::CMPL_DOUBLE: + offset = ENTRYPOINT_OFFSET(pCmplDouble); + break; + case Instruction::CMPG_DOUBLE: + offset = ENTRYPOINT_OFFSET(pCmpgDouble); + break; + default: + LOG(FATAL) << "Unexpected opcode: " << opcode; + } + FlushAllRegs(); + LockCallTemps(); + if (wide) { + LoadValueDirectWideFixed(rl_src1, rMIPS_FARG0, rMIPS_FARG1); + LoadValueDirectWideFixed(rl_src2, rMIPS_FARG2, rMIPS_FARG3); + } else { + LoadValueDirectFixed(rl_src1, rMIPS_FARG0); + LoadValueDirectFixed(rl_src2, rMIPS_FARG2); + } + int r_tgt = LoadHelper(offset); + // NOTE: not a safepoint + OpReg(kOpBlx, r_tgt); + RegLocation rl_result = GetReturn(false); + StoreValue(rl_dest, rl_result); +} + +void MipsMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, + bool gt_bias, bool is_double) +{ + UNIMPLEMENTED(FATAL) << "Need codegen for fused fp cmp branch"; +} + +void MipsMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) +{ + RegLocation rl_result; + rl_src = LoadValue(rl_src, kCoreReg); + rl_result = EvalLoc(rl_dest, kCoreReg, true); + OpRegRegImm(kOpAdd, rl_result.low_reg, rl_src.low_reg, 0x80000000); + StoreValue(rl_dest, rl_result); +} + +void MipsMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) +{ + RegLocation rl_result; + rl_src = LoadValueWide(rl_src, kCoreReg); + rl_result = EvalLoc(rl_dest, kCoreReg, true); + OpRegRegImm(kOpAdd, rl_result.high_reg, rl_src.high_reg, 0x80000000); + OpRegCopy(rl_result.low_reg, rl_src.low_reg); + StoreValueWide(rl_dest, rl_result); +} + +bool MipsMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) +{ + // TODO: need Mips implementation + return false; +} + +} // namespace art diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc new file mode 100644 index 0000000000..8bfc4e1f91 --- /dev/null +++ b/compiler/dex/quick/mips/int_mips.cc @@ -0,0 +1,659 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This file contains codegen for the Mips ISA */ + +#include "codegen_mips.h" +#include "dex/quick/mir_to_lir-inl.h" +#include "mips_lir.h" +#include "mirror/array.h" +#include "oat/runtime/oat_support_entrypoints.h" + +namespace art { + +/* + * Compare two 64-bit values + * x = y return 0 + * x < y return -1 + * x > y return 1 + * + * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0 + * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0 + * subu res, t0, t1 # res = -1:1:0 for [ < > = ] + * bnez res, finish + * sltu t0, x.lo, y.lo + * sgtu r1, x.lo, y.lo + * subu res, t0, t1 + * finish: + * + */ +void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) +{ + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + rl_src2 = LoadValueWide(rl_src2, kCoreReg); + int t0 = AllocTemp(); + int t1 = AllocTemp(); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + NewLIR3(kMipsSlt, t0, rl_src1.high_reg, rl_src2.high_reg); + NewLIR3(kMipsSlt, t1, rl_src2.high_reg, rl_src1.high_reg); + NewLIR3(kMipsSubu, rl_result.low_reg, t1, t0); + LIR* branch = OpCmpImmBranch(kCondNe, rl_result.low_reg, 0, NULL); + NewLIR3(kMipsSltu, t0, rl_src1.low_reg, rl_src2.low_reg); + NewLIR3(kMipsSltu, t1, rl_src2.low_reg, rl_src1.low_reg); + NewLIR3(kMipsSubu, rl_result.low_reg, t1, t0); + FreeTemp(t0); + FreeTemp(t1); + LIR* target = NewLIR0(kPseudoTargetLabel); + branch->target = target; + StoreValue(rl_dest, rl_result); +} + +LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, int src1, int src2, + LIR* target) +{ + LIR* branch; + MipsOpCode slt_op; + MipsOpCode br_op; + bool cmp_zero = false; + bool swapped = false; + switch (cond) { + case kCondEq: + br_op = kMipsBeq; + cmp_zero = true; + break; + case kCondNe: + br_op = kMipsBne; + cmp_zero = true; + break; + case kCondCc: + slt_op = kMipsSltu; + br_op = kMipsBnez; + break; + case kCondCs: + slt_op = kMipsSltu; + br_op = kMipsBeqz; + break; + case kCondGe: + slt_op = kMipsSlt; + br_op = kMipsBeqz; + break; + case kCondGt: + slt_op = kMipsSlt; + br_op = kMipsBnez; + swapped = true; + break; + case kCondLe: + slt_op = kMipsSlt; + br_op = kMipsBeqz; + swapped = true; + break; + case kCondLt: + slt_op = kMipsSlt; + br_op = kMipsBnez; + break; + case kCondHi: // Gtu + slt_op = kMipsSltu; + br_op = kMipsBnez; + swapped = true; + break; + default: + LOG(FATAL) << "No support for ConditionCode: " << cond; + return NULL; + } + if (cmp_zero) { + branch = NewLIR2(br_op, src1, src2); + } else { + int t_reg = AllocTemp(); + if (swapped) { + NewLIR3(slt_op, t_reg, src2, src1); + } else { + NewLIR3(slt_op, t_reg, src1, src2); + } + branch = NewLIR1(br_op, t_reg); + FreeTemp(t_reg); + } + branch->target = target; + return branch; +} + +LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, + int check_value, LIR* target) +{ + LIR* branch; + if (check_value != 0) { + // TUNING: handle s16 & kCondLt/Mi case using slti + int t_reg = AllocTemp(); + LoadConstant(t_reg, check_value); + branch = OpCmpBranch(cond, reg, t_reg, target); + FreeTemp(t_reg); + return branch; + } + MipsOpCode opc; + switch (cond) { + case kCondEq: opc = kMipsBeqz; break; + case kCondGe: opc = kMipsBgez; break; + case kCondGt: opc = kMipsBgtz; break; + case kCondLe: opc = kMipsBlez; break; + //case KCondMi: + case kCondLt: opc = kMipsBltz; break; + case kCondNe: opc = kMipsBnez; break; + default: + // Tuning: use slti when applicable + int t_reg = AllocTemp(); + LoadConstant(t_reg, check_value); + branch = OpCmpBranch(cond, reg, t_reg, target); + FreeTemp(t_reg); + return branch; + } + branch = NewLIR1(opc, reg); + branch->target = target; + return branch; +} + +LIR* MipsMir2Lir::OpRegCopyNoInsert(int r_dest, int r_src) +{ + if (MIPS_FPREG(r_dest) || MIPS_FPREG(r_src)) + return OpFpRegCopy(r_dest, r_src); + LIR* res = RawLIR(current_dalvik_offset_, kMipsMove, + r_dest, r_src); + if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { + res->flags.is_nop = true; + } + return res; +} + +LIR* MipsMir2Lir::OpRegCopy(int r_dest, int r_src) +{ + LIR *res = OpRegCopyNoInsert(r_dest, r_src); + AppendLIR(res); + return res; +} + +void MipsMir2Lir::OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, + int src_hi) +{ + bool dest_fp = MIPS_FPREG(dest_lo) && MIPS_FPREG(dest_hi); + bool src_fp = MIPS_FPREG(src_lo) && MIPS_FPREG(src_hi); + assert(MIPS_FPREG(src_lo) == MIPS_FPREG(src_hi)); + assert(MIPS_FPREG(dest_lo) == MIPS_FPREG(dest_hi)); + if (dest_fp) { + if (src_fp) { + OpRegCopy(S2d(dest_lo, dest_hi), S2d(src_lo, src_hi)); + } else { + /* note the operands are swapped for the mtc1 instr */ + NewLIR2(kMipsMtc1, src_lo, dest_lo); + NewLIR2(kMipsMtc1, src_hi, dest_hi); + } + } else { + if (src_fp) { + NewLIR2(kMipsMfc1, dest_lo, src_lo); + NewLIR2(kMipsMfc1, dest_hi, src_hi); + } else { + // Handle overlap + if (src_hi == dest_lo) { + OpRegCopy(dest_hi, src_hi); + OpRegCopy(dest_lo, src_lo); + } else { + OpRegCopy(dest_lo, src_lo); + OpRegCopy(dest_hi, src_hi); + } + } + } +} + +void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) +{ + UNIMPLEMENTED(FATAL) << "Need codegen for select"; +} + +void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) +{ + UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch"; +} + +LIR* MipsMir2Lir::GenRegMemCheck(ConditionCode c_code, + int reg1, int base, int offset, ThrowKind kind) +{ + LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm"; + return NULL; +} + +RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, int reg1, int reg2, + bool is_div) +{ + NewLIR4(kMipsDiv, r_HI, r_LO, reg1, reg2); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + if (is_div) { + NewLIR2(kMipsMflo, rl_result.low_reg, r_LO); + } else { + NewLIR2(kMipsMfhi, rl_result.low_reg, r_HI); + } + return rl_result; +} + +RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit, + bool is_div) +{ + int t_reg = AllocTemp(); + NewLIR3(kMipsAddiu, t_reg, r_ZERO, lit); + NewLIR4(kMipsDiv, r_HI, r_LO, reg1, t_reg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + if (is_div) { + NewLIR2(kMipsMflo, rl_result.low_reg, r_LO); + } else { + NewLIR2(kMipsMfhi, rl_result.low_reg, r_HI); + } + FreeTemp(t_reg); + return rl_result; +} + +void MipsMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) +{ + LOG(FATAL) << "Unexpected use of OpLea for Arm"; +} + +void MipsMir2Lir::OpTlsCmp(int offset, int val) +{ + LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm"; +} + +bool MipsMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) { + DCHECK_NE(cu_->instruction_set, kThumb2); + return false; +} + +bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) { + DCHECK_NE(cu_->instruction_set, kThumb2); + return false; +} + +LIR* MipsMir2Lir::OpPcRelLoad(int reg, LIR* target) { + LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips"; + return NULL; +} + +LIR* MipsMir2Lir::OpVldm(int rBase, int count) +{ + LOG(FATAL) << "Unexpected use of OpVldm for Mips"; + return NULL; +} + +LIR* MipsMir2Lir::OpVstm(int rBase, int count) +{ + LOG(FATAL) << "Unexpected use of OpVstm for Mips"; + return NULL; +} + +void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, + RegLocation rl_result, int lit, + int first_bit, int second_bit) +{ + int t_reg = AllocTemp(); + OpRegRegImm(kOpLsl, t_reg, rl_src.low_reg, second_bit - first_bit); + OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, t_reg); + FreeTemp(t_reg); + if (first_bit != 0) { + OpRegRegImm(kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit); + } +} + +void MipsMir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi) +{ + int t_reg = AllocTemp(); + OpRegRegReg(kOpOr, t_reg, reg_lo, reg_hi); + GenImmedCheck(kCondEq, t_reg, 0, kThrowDivZero); + FreeTemp(t_reg); +} + +// Test suspend flag, return target of taken suspend branch +LIR* MipsMir2Lir::OpTestSuspend(LIR* target) +{ + OpRegImm(kOpSub, rMIPS_SUSPEND, 1); + return OpCmpImmBranch((target == NULL) ? kCondEq : kCondNe, rMIPS_SUSPEND, 0, target); +} + +// Decrement register and branch on condition +LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, int reg, LIR* target) +{ + OpRegImm(kOpSub, reg, 1); + return OpCmpImmBranch(c_code, reg, 0, target); +} + +bool MipsMir2Lir::SmallLiteralDivide(Instruction::Code dalvik_opcode, + RegLocation rl_src, RegLocation rl_dest, int lit) +{ + LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips"; + return false; +} + +LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) +{ + LOG(FATAL) << "Unexpected use of OpIT in Mips"; + return NULL; +} + +void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) +{ + LOG(FATAL) << "Unexpected use of GenMulLong for Mips"; +} + +void MipsMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) +{ + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + rl_src2 = LoadValueWide(rl_src2, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + /* + * [v1 v0] = [a1 a0] + [a3 a2]; + * addu v0,a2,a0 + * addu t1,a3,a1 + * sltu v1,v0,a2 + * addu v1,v1,t1 + */ + + OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src2.low_reg, rl_src1.low_reg); + int t_reg = AllocTemp(); + OpRegRegReg(kOpAdd, t_reg, rl_src2.high_reg, rl_src1.high_reg); + NewLIR3(kMipsSltu, rl_result.high_reg, rl_result.low_reg, rl_src2.low_reg); + OpRegRegReg(kOpAdd, rl_result.high_reg, rl_result.high_reg, t_reg); + FreeTemp(t_reg); + StoreValueWide(rl_dest, rl_result); +} + +void MipsMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) +{ + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + rl_src2 = LoadValueWide(rl_src2, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + /* + * [v1 v0] = [a1 a0] - [a3 a2]; + * sltu t1,a0,a2 + * subu v0,a0,a2 + * subu v1,a1,a3 + * subu v1,v1,t1 + */ + + int t_reg = AllocTemp(); + NewLIR3(kMipsSltu, t_reg, rl_src1.low_reg, rl_src2.low_reg); + OpRegRegReg(kOpSub, rl_result.low_reg, rl_src1.low_reg, rl_src2.low_reg); + OpRegRegReg(kOpSub, rl_result.high_reg, rl_src1.high_reg, rl_src2.high_reg); + OpRegRegReg(kOpSub, rl_result.high_reg, rl_result.high_reg, t_reg); + FreeTemp(t_reg); + StoreValueWide(rl_dest, rl_result); +} + +void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) +{ + rl_src = LoadValueWide(rl_src, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + /* + * [v1 v0] = -[a1 a0] + * negu v0,a0 + * negu v1,a1 + * sltu t1,r_zero + * subu v1,v1,t1 + */ + + OpRegReg(kOpNeg, rl_result.low_reg, rl_src.low_reg); + OpRegReg(kOpNeg, rl_result.high_reg, rl_src.high_reg); + int t_reg = AllocTemp(); + NewLIR3(kMipsSltu, t_reg, r_ZERO, rl_result.low_reg); + OpRegRegReg(kOpSub, rl_result.high_reg, rl_result.high_reg, t_reg); + FreeTemp(t_reg); + StoreValueWide(rl_dest, rl_result); +} + +void MipsMir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) +{ + LOG(FATAL) << "Unexpected use of GenAndLong for Mips"; +} + +void MipsMir2Lir::GenOrLong(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) +{ + LOG(FATAL) << "Unexpected use of GenOrLong for Mips"; +} + +void MipsMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) +{ + LOG(FATAL) << "Unexpected use of GenXorLong for Mips"; +} + +/* + * Generate array load + */ +void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, + RegLocation rl_index, RegLocation rl_dest, int scale) +{ + RegisterClass reg_class = oat_reg_class_by_size(size); + int len_offset = mirror::Array::LengthOffset().Int32Value(); + int data_offset; + RegLocation rl_result; + rl_array = LoadValue(rl_array, kCoreReg); + rl_index = LoadValue(rl_index, kCoreReg); + + if (size == kLong || size == kDouble) { + data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); + } else { + data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); + } + + /* null object? */ + GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags); + + int reg_ptr = AllocTemp(); + bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); + int reg_len = INVALID_REG; + if (needs_range_check) { + reg_len = AllocTemp(); + /* Get len */ + LoadWordDisp(rl_array.low_reg, len_offset, reg_len); + } + /* reg_ptr -> array data */ + OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset); + FreeTemp(rl_array.low_reg); + if ((size == kLong) || (size == kDouble)) { + if (scale) { + int r_new_index = AllocTemp(); + OpRegRegImm(kOpLsl, r_new_index, rl_index.low_reg, scale); + OpRegReg(kOpAdd, reg_ptr, r_new_index); + FreeTemp(r_new_index); + } else { + OpRegReg(kOpAdd, reg_ptr, rl_index.low_reg); + } + FreeTemp(rl_index.low_reg); + rl_result = EvalLoc(rl_dest, reg_class, true); + + if (needs_range_check) { + // TODO: change kCondCS to a more meaningful name, is the sense of + // carry-set/clear flipped? + GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + FreeTemp(reg_len); + } + LoadBaseDispWide(reg_ptr, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); + + FreeTemp(reg_ptr); + StoreValueWide(rl_dest, rl_result); + } else { + rl_result = EvalLoc(rl_dest, reg_class, true); + + if (needs_range_check) { + // TODO: change kCondCS to a more meaningful name, is the sense of + // carry-set/clear flipped? + GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + FreeTemp(reg_len); + } + LoadBaseIndexed(reg_ptr, rl_index.low_reg, rl_result.low_reg, scale, size); + + FreeTemp(reg_ptr); + StoreValue(rl_dest, rl_result); + } +} + +/* + * Generate array store + * + */ +void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, + RegLocation rl_index, RegLocation rl_src, int scale) +{ + RegisterClass reg_class = oat_reg_class_by_size(size); + int len_offset = mirror::Array::LengthOffset().Int32Value(); + int data_offset; + + if (size == kLong || size == kDouble) { + data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); + } else { + data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); + } + + rl_array = LoadValue(rl_array, kCoreReg); + rl_index = LoadValue(rl_index, kCoreReg); + int reg_ptr = INVALID_REG; + if (IsTemp(rl_array.low_reg)) { + Clobber(rl_array.low_reg); + reg_ptr = rl_array.low_reg; + } else { + reg_ptr = AllocTemp(); + OpRegCopy(reg_ptr, rl_array.low_reg); + } + + /* null object? */ + GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags); + + bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); + int reg_len = INVALID_REG; + if (needs_range_check) { + reg_len = AllocTemp(); + //NOTE: max live temps(4) here. + /* Get len */ + LoadWordDisp(rl_array.low_reg, len_offset, reg_len); + } + /* reg_ptr -> array data */ + OpRegImm(kOpAdd, reg_ptr, data_offset); + /* at this point, reg_ptr points to array, 2 live temps */ + if ((size == kLong) || (size == kDouble)) { + //TUNING: specific wide routine that can handle fp regs + if (scale) { + int r_new_index = AllocTemp(); + OpRegRegImm(kOpLsl, r_new_index, rl_index.low_reg, scale); + OpRegReg(kOpAdd, reg_ptr, r_new_index); + FreeTemp(r_new_index); + } else { + OpRegReg(kOpAdd, reg_ptr, rl_index.low_reg); + } + rl_src = LoadValueWide(rl_src, reg_class); + + if (needs_range_check) { + GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + FreeTemp(reg_len); + } + + StoreBaseDispWide(reg_ptr, 0, rl_src.low_reg, rl_src.high_reg); + + FreeTemp(reg_ptr); + } else { + rl_src = LoadValue(rl_src, reg_class); + if (needs_range_check) { + GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + FreeTemp(reg_len); + } + StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg, + scale, size); + } +} + +/* + * Generate array store + * + */ +void MipsMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array, + RegLocation rl_index, RegLocation rl_src, int scale) +{ + int len_offset = mirror::Array::LengthOffset().Int32Value(); + int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(); + + FlushAllRegs(); // Use explicit registers + LockCallTemps(); + + int r_value = TargetReg(kArg0); // Register holding value + int r_array_class = TargetReg(kArg1); // Register holding array's Class + int r_array = TargetReg(kArg2); // Register holding array + int r_index = TargetReg(kArg3); // Register holding index into array + + LoadValueDirectFixed(rl_array, r_array); // Grab array + LoadValueDirectFixed(rl_src, r_value); // Grab value + LoadValueDirectFixed(rl_index, r_index); // Grab index + + GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE? + + // Store of null? + LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL); + + // Get the array's class. + LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class); + CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode), r_value, + r_array_class, true); + // Redo LoadValues in case they didn't survive the call. + LoadValueDirectFixed(rl_array, r_array); // Reload array + LoadValueDirectFixed(rl_index, r_index); // Reload index + LoadValueDirectFixed(rl_src, r_value); // Reload value + r_array_class = INVALID_REG; + + // Branch here if value to be stored == null + LIR* target = NewLIR0(kPseudoTargetLabel); + null_value_check->target = target; + + bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); + int reg_len = INVALID_REG; + if (needs_range_check) { + reg_len = TargetReg(kArg1); + LoadWordDisp(r_array, len_offset, reg_len); // Get len + } + /* r_ptr -> array data */ + int r_ptr = AllocTemp(); + OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset); + if (needs_range_check) { + GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds); + } + StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord); + FreeTemp(r_ptr); + FreeTemp(r_index); + if (!mir_graph_->IsConstantNullRef(rl_src)) { + MarkGCCard(r_value, r_array); + } +} + +void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_shift) +{ + // Default implementation is just to ignore the constant case. + GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); +} + +void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) +{ + // Default - bail to non-const handler. + GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2); +} + +} // namespace art diff --git a/compiler/dex/quick/mips/mips_lir.h b/compiler/dex/quick/mips/mips_lir.h new file mode 100644 index 0000000000..ceab9ab1e5 --- /dev/null +++ b/compiler/dex/quick/mips/mips_lir.h @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_COMPILER_DEX_QUICK_MIPS_MIPSLIR_H_ +#define ART_SRC_COMPILER_DEX_QUICK_MIPS_MIPSLIR_H_ + +#include "dex/compiler_internals.h" + +namespace art { + +/* + * Runtime register conventions. + * + * zero is always the value 0 + * at is scratch (normally used as temp reg by assembler) + * v0, v1 are scratch (normally hold subroutine return values) + * a0-a3 are scratch (normally hold subroutine arguments) + * t0-t8 are scratch + * t9 is scratch (normally used for function calls) + * s0 (rMIPS_SUSPEND) is reserved [holds suspend-check counter] + * s1 (rMIPS_SELF) is reserved [holds current &Thread] + * s2-s7 are callee save (promotion target) + * k0, k1 are reserved for use by interrupt handlers + * gp is reserved for global pointer + * sp is reserved + * s8 is callee save (promotion target) + * ra is scratch (normally holds the return addr) + * + * Preserved across C calls: s0-s8 + * Trashed across C calls: at, v0-v1, a0-a3, t0-t9, gp, ra + * + * Floating pointer registers + * NOTE: there are 32 fp registers (16 df pairs), but currently + * only support 16 fp registers (8 df pairs). + * f0-f15 + * df0-df7, where df0={f0,f1}, df1={f2,f3}, ... , df7={f14,f15} + * + * f0-f15 (df0-df7) trashed across C calls + * + * For mips32 code use: + * a0-a3 to hold operands + * v0-v1 to hold results + * t0-t9 for temps + * + * All jump/branch instructions have a delay slot after it. + * + * Stack frame diagram (stack grows down, higher addresses at top): + * + * +------------------------+ + * | IN[ins-1] | {Note: resides in caller's frame} + * | . | + * | IN[0] | + * | caller's Method* | + * +========================+ {Note: start of callee's frame} + * | spill region | {variable sized - will include lr if non-leaf.} + * +------------------------+ + * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long] + * +------------------------+ + * | V[locals-1] | + * | V[locals-2] | + * | . | + * | . | + * | V[1] | + * | V[0] | + * +------------------------+ + * | 0 to 3 words padding | + * +------------------------+ + * | OUT[outs-1] | + * | OUT[outs-2] | + * | . | + * | OUT[0] | + * | cur_method* | <<== sp w/ 16-byte alignment + * +========================+ + */ + +// Offset to distingish FP regs. +#define MIPS_FP_REG_OFFSET 32 +// Offset to distinguish DP FP regs. +#define MIPS_FP_DOUBLE 64 +// Offset to distingish the extra regs. +#define MIPS_EXTRA_REG_OFFSET 128 +// Reg types. +#define MIPS_REGTYPE(x) (x & (MIPS_FP_REG_OFFSET | MIPS_FP_DOUBLE)) +#define MIPS_FPREG(x) ((x & MIPS_FP_REG_OFFSET) == MIPS_FP_REG_OFFSET) +#define MIPS_EXTRAREG(x) ((x & MIPS_EXTRA_REG_OFFSET) == MIPS_EXTRA_REG_OFFSET) +#define MIPS_DOUBLEREG(x) ((x & MIPS_FP_DOUBLE) == MIPS_FP_DOUBLE) +#define MIPS_SINGLEREG(x) (MIPS_FPREG(x) && !MIPS_DOUBLEREG(x)) +/* + * Note: the low register of a floating point pair is sufficient to + * create the name of a double, but require both names to be passed to + * allow for asserts to verify that the pair is consecutive if significant + * rework is done in this area. Also, it is a good reminder in the calling + * code that reg locations always describe doubles as a pair of singles. + */ +#define MIPS_S2D(x,y) ((x) | MIPS_FP_DOUBLE) +// Mask to strip off fp flags. +#define MIPS_FP_REG_MASK (MIPS_FP_REG_OFFSET-1) + +#ifdef HAVE_LITTLE_ENDIAN +#define LOWORD_OFFSET 0 +#define HIWORD_OFFSET 4 +#define r_ARG0 r_A0 +#define r_ARG1 r_A1 +#define r_ARG2 r_A2 +#define r_ARG3 r_A3 +#define r_RESULT0 r_V0 +#define r_RESULT1 r_V1 +#else +#define LOWORD_OFFSET 4 +#define HIWORD_OFFSET 0 +#define r_ARG0 r_A1 +#define r_ARG1 r_A0 +#define r_ARG2 r_A3 +#define r_ARG3 r_A2 +#define r_RESULT0 r_V1 +#define r_RESULT1 r_V0 +#endif + +// These are the same for both big and little endian. +#define r_FARG0 r_F12 +#define r_FARG1 r_F13 +#define r_FARG2 r_F14 +#define r_FARG3 r_F15 +#define r_FRESULT0 r_F0 +#define r_FRESULT1 r_F1 + +// Regs not used for Mips. +#define rMIPS_LR INVALID_REG +#define rMIPS_PC INVALID_REG + +// RegisterLocation templates return values (r_V0, or r_V0/r_V1). +#define MIPS_LOC_C_RETURN {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, r_V0, INVALID_REG, \ + INVALID_SREG, INVALID_SREG} +#define MIPS_LOC_C_RETURN_FLOAT {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, r_FRESULT0, \ + INVALID_REG, INVALID_SREG, INVALID_SREG} +#define MIPS_LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r_RESULT0, \ + r_RESULT1, INVALID_SREG, INVALID_SREG} +#define MIPS_LOC_C_RETURN_DOUBLE {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r_FRESULT0,\ + r_FRESULT1, INVALID_SREG, INVALID_SREG} + +enum MipsResourceEncodingPos { + kMipsGPReg0 = 0, + kMipsRegSP = 29, + kMipsRegLR = 31, + kMipsFPReg0 = 32, // only 16 fp regs supported currently. + kMipsFPRegEnd = 48, + kMipsRegHI = kMipsFPRegEnd, + kMipsRegLO, + kMipsRegPC, + kMipsRegEnd = 51, +}; + +#define ENCODE_MIPS_REG_LIST(N) (static_cast<uint64_t>(N)) +#define ENCODE_MIPS_REG_SP (1ULL << kMipsRegSP) +#define ENCODE_MIPS_REG_LR (1ULL << kMipsRegLR) +#define ENCODE_MIPS_REG_PC (1ULL << kMipsRegPC) + +enum MipsNativeRegisterPool { + r_ZERO = 0, + r_AT = 1, + r_V0 = 2, + r_V1 = 3, + r_A0 = 4, + r_A1 = 5, + r_A2 = 6, + r_A3 = 7, + r_T0 = 8, + r_T1 = 9, + r_T2 = 10, + r_T3 = 11, + r_T4 = 12, + r_T5 = 13, + r_T6 = 14, + r_T7 = 15, + r_S0 = 16, + r_S1 = 17, + r_S2 = 18, + r_S3 = 19, + r_S4 = 20, + r_S5 = 21, + r_S6 = 22, + r_S7 = 23, + r_T8 = 24, + r_T9 = 25, + r_K0 = 26, + r_K1 = 27, + r_GP = 28, + r_SP = 29, + r_FP = 30, + r_RA = 31, + + r_F0 = 0 + MIPS_FP_REG_OFFSET, + r_F1, + r_F2, + r_F3, + r_F4, + r_F5, + r_F6, + r_F7, + r_F8, + r_F9, + r_F10, + r_F11, + r_F12, + r_F13, + r_F14, + r_F15, +#if 0 + /* + * TODO: The shared resource mask doesn't have enough bit positions to describe all + * MIPS registers. Expand it and enable use of fp registers 16 through 31. + */ + r_F16, + r_F17, + r_F18, + r_F19, + r_F20, + r_F21, + r_F22, + r_F23, + r_F24, + r_F25, + r_F26, + r_F27, + r_F28, + r_F29, + r_F30, + r_F31, +#endif + r_DF0 = r_F0 + MIPS_FP_DOUBLE, + r_DF1 = r_F2 + MIPS_FP_DOUBLE, + r_DF2 = r_F4 + MIPS_FP_DOUBLE, + r_DF3 = r_F6 + MIPS_FP_DOUBLE, + r_DF4 = r_F8 + MIPS_FP_DOUBLE, + r_DF5 = r_F10 + MIPS_FP_DOUBLE, + r_DF6 = r_F12 + MIPS_FP_DOUBLE, + r_DF7 = r_F14 + MIPS_FP_DOUBLE, +#if 0 // TODO: expand resource mask to enable use of all MIPS fp registers. + r_DF8 = r_F16 + MIPS_FP_DOUBLE, + r_DF9 = r_F18 + MIPS_FP_DOUBLE, + r_DF10 = r_F20 + MIPS_FP_DOUBLE, + r_DF11 = r_F22 + MIPS_FP_DOUBLE, + r_DF12 = r_F24 + MIPS_FP_DOUBLE, + r_DF13 = r_F26 + MIPS_FP_DOUBLE, + r_DF14 = r_F28 + MIPS_FP_DOUBLE, + r_DF15 = r_F30 + MIPS_FP_DOUBLE, +#endif + r_HI = MIPS_EXTRA_REG_OFFSET, + r_LO, + r_PC, +}; + +#define rMIPS_SUSPEND r_S0 +#define rMIPS_SELF r_S1 +#define rMIPS_SP r_SP +#define rMIPS_ARG0 r_ARG0 +#define rMIPS_ARG1 r_ARG1 +#define rMIPS_ARG2 r_ARG2 +#define rMIPS_ARG3 r_ARG3 +#define rMIPS_FARG0 r_FARG0 +#define rMIPS_FARG1 r_FARG1 +#define rMIPS_FARG2 r_FARG2 +#define rMIPS_FARG3 r_FARG3 +#define rMIPS_RET0 r_RESULT0 +#define rMIPS_RET1 r_RESULT1 +#define rMIPS_INVOKE_TGT r_T9 +#define rMIPS_COUNT INVALID_REG + +enum MipsShiftEncodings { + kMipsLsl = 0x0, + kMipsLsr = 0x1, + kMipsAsr = 0x2, + kMipsRor = 0x3 +}; + +// MIPS sync kinds (Note: support for kinds other than kSYNC0 may not exist). +#define kSYNC0 0x00 +#define kSYNC_WMB 0x04 +#define kSYNC_MB 0x01 +#define kSYNC_ACQUIRE 0x11 +#define kSYNC_RELEASE 0x12 +#define kSYNC_RMB 0x13 + +// TODO: Use smaller hammer when appropriate for target CPU. +#define kST kSYNC0 +#define kSY kSYNC0 + +/* + * The following enum defines the list of supported Thumb instructions by the + * assembler. Their corresponding EncodingMap positions will be defined in + * Assemble.cc. + */ +enum MipsOpCode { + kMipsFirst = 0, + kMips32BitData = kMipsFirst, // data [31..0]. + kMipsAddiu, // addiu t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0]. + kMipsAddu, // add d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100001]. + kMipsAnd, // and d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100100]. + kMipsAndi, // andi t,s,imm16 [001100] s[25..21] t[20..16] imm16[15..0]. + kMipsB, // b o [0001000000000000] o[15..0]. + kMipsBal, // bal o [0000010000010001] o[15..0]. + // NOTE: the code tests the range kMipsBeq thru kMipsBne, so adding an instruction in this + // range may require updates. + kMipsBeq, // beq s,t,o [000100] s[25..21] t[20..16] o[15..0]. + kMipsBeqz, // beqz s,o [000100] s[25..21] [00000] o[15..0]. + kMipsBgez, // bgez s,o [000001] s[25..21] [00001] o[15..0]. + kMipsBgtz, // bgtz s,o [000111] s[25..21] [00000] o[15..0]. + kMipsBlez, // blez s,o [000110] s[25..21] [00000] o[15..0]. + kMipsBltz, // bltz s,o [000001] s[25..21] [00000] o[15..0]. + kMipsBnez, // bnez s,o [000101] s[25..21] [00000] o[15..0]. + kMipsBne, // bne s,t,o [000101] s[25..21] t[20..16] o[15..0]. + kMipsDiv, // div s,t [000000] s[25..21] t[20..16] [0000000000011010]. +#if __mips_isa_rev>=2 + kMipsExt, // ext t,s,p,z [011111] s[25..21] t[20..16] z[15..11] p[10..6] [000000]. +#endif + kMipsJal, // jal t [000011] t[25..0]. + kMipsJalr, // jalr d,s [000000] s[25..21] [00000] d[15..11] hint[10..6] [001001]. + kMipsJr, // jr s [000000] s[25..21] [0000000000] hint[10..6] [001000]. + kMipsLahi, // lui t,imm16 [00111100000] t[20..16] imm16[15..0] load addr hi. + kMipsLalo, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] load addr lo. + kMipsLui, // lui t,imm16 [00111100000] t[20..16] imm16[15..0]. + kMipsLb, // lb t,o(b) [100000] b[25..21] t[20..16] o[15..0]. + kMipsLbu, // lbu t,o(b) [100100] b[25..21] t[20..16] o[15..0]. + kMipsLh, // lh t,o(b) [100001] b[25..21] t[20..16] o[15..0]. + kMipsLhu, // lhu t,o(b) [100101] b[25..21] t[20..16] o[15..0]. + kMipsLw, // lw t,o(b) [100011] b[25..21] t[20..16] o[15..0]. + kMipsMfhi, // mfhi d [0000000000000000] d[15..11] [00000010000]. + kMipsMflo, // mflo d [0000000000000000] d[15..11] [00000010010]. + kMipsMove, // move d,s [000000] s[25..21] [00000] d[15..11] [00000100101]. + kMipsMovz, // movz d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000001010]. + kMipsMul, // mul d,s,t [011100] s[25..21] t[20..16] d[15..11] [00000000010]. + kMipsNop, // nop [00000000000000000000000000000000]. + kMipsNor, // nor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100111]. + kMipsOr, // or d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100101]. + kMipsOri, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0]. + kMipsPref, // pref h,o(b) [101011] b[25..21] h[20..16] o[15..0]. + kMipsSb, // sb t,o(b) [101000] b[25..21] t[20..16] o[15..0]. +#if __mips_isa_rev>=2 + kMipsSeb, // seb d,t [01111100000] t[20..16] d[15..11] [10000100000]. + kMipsSeh, // seh d,t [01111100000] t[20..16] d[15..11] [11000100000]. +#endif + kMipsSh, // sh t,o(b) [101001] b[25..21] t[20..16] o[15..0]. + kMipsSll, // sll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [000000]. + kMipsSllv, // sllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000100]. + kMipsSlt, // slt d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101010]. + kMipsSlti, // slti t,s,imm16 [001010] s[25..21] t[20..16] imm16[15..0]. + kMipsSltu, // sltu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101011]. + kMipsSra, // sra d,s,imm5 [00000000000] t[20..16] d[15..11] imm5[10..6] [000011]. + kMipsSrav, // srav d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000111]. + kMipsSrl, // srl d,t,a [00000000000] t[20..16] d[20..16] a[10..6] [000010]. + kMipsSrlv, // srlv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000110]. + kMipsSubu, // subu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100011]. + kMipsSw, // sw t,o(b) [101011] b[25..21] t[20..16] o[15..0]. + kMipsXor, // xor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100110]. + kMipsXori, // xori t,s,imm16 [001110] s[25..21] t[20..16] imm16[15..0]. + kMipsFadds, // add.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000000]. + kMipsFsubs, // sub.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000001]. + kMipsFmuls, // mul.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000010]. + kMipsFdivs, // div.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000011]. + kMipsFaddd, // add.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000000]. + kMipsFsubd, // sub.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000001]. + kMipsFmuld, // mul.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000010]. + kMipsFdivd, // div.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000011]. + kMipsFcvtsd,// cvt.s.d d,s [01000110001] [00000] s[15..11] d[10..6] [100000]. + kMipsFcvtsw,// cvt.s.w d,s [01000110100] [00000] s[15..11] d[10..6] [100000]. + kMipsFcvtds,// cvt.d.s d,s [01000110000] [00000] s[15..11] d[10..6] [100001]. + kMipsFcvtdw,// cvt.d.w d,s [01000110100] [00000] s[15..11] d[10..6] [100001]. + kMipsFcvtws,// cvt.w.d d,s [01000110000] [00000] s[15..11] d[10..6] [100100]. + kMipsFcvtwd,// cvt.w.d d,s [01000110001] [00000] s[15..11] d[10..6] [100100]. + kMipsFmovs, // mov.s d,s [01000110000] [00000] s[15..11] d[10..6] [000110]. + kMipsFmovd, // mov.d d,s [01000110001] [00000] s[15..11] d[10..6] [000110]. + kMipsFlwc1, // lwc1 t,o(b) [110001] b[25..21] t[20..16] o[15..0]. + kMipsFldc1, // ldc1 t,o(b) [110101] b[25..21] t[20..16] o[15..0]. + kMipsFswc1, // swc1 t,o(b) [111001] b[25..21] t[20..16] o[15..0]. + kMipsFsdc1, // sdc1 t,o(b) [111101] b[25..21] t[20..16] o[15..0]. + kMipsMfc1, // mfc1 t,s [01000100000] t[20..16] s[15..11] [00000000000]. + kMipsMtc1, // mtc1 t,s [01000100100] t[20..16] s[15..11] [00000000000]. + kMipsDelta, // Psuedo for ori t, s, <label>-<label>. + kMipsDeltaHi, // Pseudo for lui t, high16(<label>-<label>). + kMipsDeltaLo, // Pseudo for ori t, s, low16(<label>-<label>). + kMipsCurrPC, // jal to .+8 to materialize pc. + kMipsSync, // sync kind [000000] [0000000000000000] s[10..6] [001111]. + kMipsUndefined, // undefined [011001xxxxxxxxxxxxxxxx]. + kMipsLast +}; + +// Instruction assembly field_loc kind. +enum MipsEncodingKind { + kFmtUnused, + kFmtBitBlt, /* Bit string using end/start */ + kFmtDfp, /* Double FP reg */ + kFmtSfp, /* Single FP reg */ + kFmtBlt5_2, /* Same 5-bit field to 2 locations */ +}; + +// Struct used to define the snippet positions for each MIPS opcode. +struct MipsEncodingMap { + uint32_t skeleton; + struct { + MipsEncodingKind kind; + int end; // end for kFmtBitBlt, 1-bit slice end for FP regs. + int start; // start for kFmtBitBlt, 4-bit slice end for FP regs. + } field_loc[4]; + MipsOpCode opcode; + uint64_t flags; + const char *name; + const char* fmt; + int size; // Note: size is in bytes. +}; + +extern MipsEncodingMap EncodingMap[kMipsLast]; + +#define IS_UIMM16(v) ((0 <= (v)) && ((v) <= 65535)) +#define IS_SIMM16(v) ((-32768 <= (v)) && ((v) <= 32766)) +#define IS_SIMM16_2WORD(v) ((-32764 <= (v)) && ((v) <= 32763)) // 2 offsets must fit. + +} // namespace art + +#endif // ART_SRC_COMPILER_DEX_QUICK_MIPS_MIPSLIR_H_ diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc new file mode 100644 index 0000000000..cab2c1b53d --- /dev/null +++ b/compiler/dex/quick/mips/target_mips.cc @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "codegen_mips.h" +#include "dex/compiler_internals.h" +#include "dex/quick/mir_to_lir-inl.h" +#include "mips_lir.h" + +#include <string> + +namespace art { + +static int core_regs[] = {r_ZERO, r_AT, r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, + r_T0, r_T1, r_T2, r_T3, r_T4, r_T5, r_T6, r_T7, + r_S0, r_S1, r_S2, r_S3, r_S4, r_S5, r_S6, r_S7, r_T8, + r_T9, r_K0, r_K1, r_GP, r_SP, r_FP, r_RA}; +static int ReservedRegs[] = {r_ZERO, r_AT, r_S0, r_S1, r_K0, r_K1, r_GP, r_SP, + r_RA}; +static int core_temps[] = {r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, r_T0, r_T1, r_T2, + r_T3, r_T4, r_T5, r_T6, r_T7, r_T8}; +static int FpRegs[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7, + r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15}; +static int fp_temps[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7, + r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15}; + +RegLocation MipsMir2Lir::LocCReturn() +{ + RegLocation res = MIPS_LOC_C_RETURN; + return res; +} + +RegLocation MipsMir2Lir::LocCReturnWide() +{ + RegLocation res = MIPS_LOC_C_RETURN_WIDE; + return res; +} + +RegLocation MipsMir2Lir::LocCReturnFloat() +{ + RegLocation res = MIPS_LOC_C_RETURN_FLOAT; + return res; +} + +RegLocation MipsMir2Lir::LocCReturnDouble() +{ + RegLocation res = MIPS_LOC_C_RETURN_DOUBLE; + return res; +} + +// Return a target-dependent special register. +int MipsMir2Lir::TargetReg(SpecialTargetRegister reg) { + int res = INVALID_REG; + switch (reg) { + case kSelf: res = rMIPS_SELF; break; + case kSuspend: res = rMIPS_SUSPEND; break; + case kLr: res = rMIPS_LR; break; + case kPc: res = rMIPS_PC; break; + case kSp: res = rMIPS_SP; break; + case kArg0: res = rMIPS_ARG0; break; + case kArg1: res = rMIPS_ARG1; break; + case kArg2: res = rMIPS_ARG2; break; + case kArg3: res = rMIPS_ARG3; break; + case kFArg0: res = rMIPS_FARG0; break; + case kFArg1: res = rMIPS_FARG1; break; + case kFArg2: res = rMIPS_FARG2; break; + case kFArg3: res = rMIPS_FARG3; break; + case kRet0: res = rMIPS_RET0; break; + case kRet1: res = rMIPS_RET1; break; + case kInvokeTgt: res = rMIPS_INVOKE_TGT; break; + case kCount: res = rMIPS_COUNT; break; + } + return res; +} + +// Create a double from a pair of singles. +int MipsMir2Lir::S2d(int low_reg, int high_reg) +{ + return MIPS_S2D(low_reg, high_reg); +} + +// Return mask to strip off fp reg flags and bias. +uint32_t MipsMir2Lir::FpRegMask() +{ + return MIPS_FP_REG_MASK; +} + +// True if both regs single, both core or both double. +bool MipsMir2Lir::SameRegType(int reg1, int reg2) +{ + return (MIPS_REGTYPE(reg1) == MIPS_REGTYPE(reg2)); +} + +/* + * Decode the register id. + */ +uint64_t MipsMir2Lir::GetRegMaskCommon(int reg) +{ + uint64_t seed; + int shift; + int reg_id; + + + reg_id = reg & 0x1f; + /* Each double register is equal to a pair of single-precision FP registers */ + seed = MIPS_DOUBLEREG(reg) ? 3 : 1; + /* FP register starts at bit position 16 */ + shift = MIPS_FPREG(reg) ? kMipsFPReg0 : 0; + /* Expand the double register id into single offset */ + shift += reg_id; + return (seed << shift); +} + +uint64_t MipsMir2Lir::GetPCUseDefEncoding() +{ + return ENCODE_MIPS_REG_PC; +} + + +void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir) +{ + DCHECK_EQ(cu_->instruction_set, kMips); + + // Mips-specific resource map setup here. + uint64_t flags = MipsMir2Lir::EncodingMap[lir->opcode].flags; + + if (flags & REG_DEF_SP) { + lir->def_mask |= ENCODE_MIPS_REG_SP; + } + + if (flags & REG_USE_SP) { + lir->use_mask |= ENCODE_MIPS_REG_SP; + } + + if (flags & REG_DEF_LR) { + lir->def_mask |= ENCODE_MIPS_REG_LR; + } +} + +/* For dumping instructions */ +#define MIPS_REG_COUNT 32 +static const char *mips_reg_name[MIPS_REG_COUNT] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" +}; + +/* + * Interpret a format string and build a string no longer than size + * See format key in Assemble.c. + */ +std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char* base_addr) +{ + std::string buf; + int i; + const char *fmt_end = &fmt[strlen(fmt)]; + char tbuf[256]; + char nc; + while (fmt < fmt_end) { + int operand; + if (*fmt == '!') { + fmt++; + DCHECK_LT(fmt, fmt_end); + nc = *fmt++; + if (nc=='!') { + strcpy(tbuf, "!"); + } else { + DCHECK_LT(fmt, fmt_end); + DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u); + operand = lir->operands[nc-'0']; + switch (*fmt++) { + case 'b': + strcpy(tbuf,"0000"); + for (i=3; i>= 0; i--) { + tbuf[i] += operand & 1; + operand >>= 1; + } + break; + case 's': + sprintf(tbuf,"$f%d",operand & MIPS_FP_REG_MASK); + break; + case 'S': + DCHECK_EQ(((operand & MIPS_FP_REG_MASK) & 1), 0); + sprintf(tbuf,"$f%d",operand & MIPS_FP_REG_MASK); + break; + case 'h': + sprintf(tbuf,"%04x", operand); + break; + case 'M': + case 'd': + sprintf(tbuf,"%d", operand); + break; + case 'D': + sprintf(tbuf,"%d", operand+1); + break; + case 'E': + sprintf(tbuf,"%d", operand*4); + break; + case 'F': + sprintf(tbuf,"%d", operand*2); + break; + case 't': + sprintf(tbuf,"0x%08x (L%p)", reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + + (operand << 2), lir->target); + break; + case 'T': + sprintf(tbuf,"0x%08x", operand << 2); + break; + case 'u': { + int offset_1 = lir->operands[0]; + int offset_2 = NEXT_LIR(lir)->operands[0]; + uintptr_t target = + (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) + + (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc; + sprintf(tbuf, "%p", reinterpret_cast<void*>(target)); + break; + } + + /* Nothing to print for BLX_2 */ + case 'v': + strcpy(tbuf, "see above"); + break; + case 'r': + DCHECK(operand >= 0 && operand < MIPS_REG_COUNT); + strcpy(tbuf, mips_reg_name[operand]); + break; + case 'N': + // Placeholder for delay slot handling + strcpy(tbuf, "; nop"); + break; + default: + strcpy(tbuf,"DecodeError"); + break; + } + buf += tbuf; + } + } else { + buf += *fmt++; + } + } + return buf; +} + +// FIXME: need to redo resource maps for MIPS - fix this at that time +void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, uint64_t mask, const char *prefix) +{ + char buf[256]; + buf[0] = 0; + + if (mask == ENCODE_ALL) { + strcpy(buf, "all"); + } else { + char num[8]; + int i; + + for (i = 0; i < kMipsRegEnd; i++) { + if (mask & (1ULL << i)) { + sprintf(num, "%d ", i); + strcat(buf, num); + } + } + + if (mask & ENCODE_CCODE) { + strcat(buf, "cc "); + } + if (mask & ENCODE_FP_STATUS) { + strcat(buf, "fpcc "); + } + /* Memory bits */ + if (mips_lir && (mask & ENCODE_DALVIK_REG)) { + sprintf(buf + strlen(buf), "dr%d%s", mips_lir->alias_info & 0xffff, + (mips_lir->alias_info & 0x80000000) ? "(+1)" : ""); + } + if (mask & ENCODE_LITERAL) { + strcat(buf, "lit "); + } + + if (mask & ENCODE_HEAP_REF) { + strcat(buf, "heap "); + } + if (mask & ENCODE_MUST_NOT_ALIAS) { + strcat(buf, "noalias "); + } + } + if (buf[0]) { + LOG(INFO) << prefix << ": " << buf; + } +} + +/* + * TUNING: is true leaf? Can't just use METHOD_IS_LEAF to determine as some + * instructions might call out to C/assembly helper functions. Until + * machinery is in place, always spill lr. + */ + +void MipsMir2Lir::AdjustSpillMask() +{ + core_spill_mask_ |= (1 << r_RA); + num_core_spills_++; +} + +/* + * Mark a callee-save fp register as promoted. Note that + * vpush/vpop uses contiguous register lists so we must + * include any holes in the mask. Associate holes with + * Dalvik register INVALID_VREG (0xFFFFU). + */ +void MipsMir2Lir::MarkPreservedSingle(int s_reg, int reg) +{ + LOG(FATAL) << "No support yet for promoted FP regs"; +} + +void MipsMir2Lir::FlushRegWide(int reg1, int reg2) +{ + RegisterInfo* info1 = GetRegInfo(reg1); + RegisterInfo* info2 = GetRegInfo(reg2); + DCHECK(info1 && info2 && info1->pair && info2->pair && + (info1->partner == info2->reg) && + (info2->partner == info1->reg)); + if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) { + if (!(info1->is_temp && info2->is_temp)) { + /* Should not happen. If it does, there's a problem in eval_loc */ + LOG(FATAL) << "Long half-temp, half-promoted"; + } + + info1->dirty = false; + info2->dirty = false; + if (mir_graph_->SRegToVReg(info2->s_reg) < mir_graph_->SRegToVReg(info1->s_reg)) + info1 = info2; + int v_reg = mir_graph_->SRegToVReg(info1->s_reg); + StoreBaseDispWide(rMIPS_SP, VRegOffset(v_reg), info1->reg, info1->partner); + } +} + +void MipsMir2Lir::FlushReg(int reg) +{ + RegisterInfo* info = GetRegInfo(reg); + if (info->live && info->dirty) { + info->dirty = false; + int v_reg = mir_graph_->SRegToVReg(info->s_reg); + StoreBaseDisp(rMIPS_SP, VRegOffset(v_reg), reg, kWord); + } +} + +/* Give access to the target-dependent FP register encoding to common code */ +bool MipsMir2Lir::IsFpReg(int reg) { + return MIPS_FPREG(reg); +} + +/* Clobber all regs that might be used by an external C call */ +void MipsMir2Lir::ClobberCalleeSave() +{ + Clobber(r_ZERO); + Clobber(r_AT); + Clobber(r_V0); + Clobber(r_V1); + Clobber(r_A0); + Clobber(r_A1); + Clobber(r_A2); + Clobber(r_A3); + Clobber(r_T0); + Clobber(r_T1); + Clobber(r_T2); + Clobber(r_T3); + Clobber(r_T4); + Clobber(r_T5); + Clobber(r_T6); + Clobber(r_T7); + Clobber(r_T8); + Clobber(r_T9); + Clobber(r_K0); + Clobber(r_K1); + Clobber(r_GP); + Clobber(r_FP); + Clobber(r_RA); + Clobber(r_F0); + Clobber(r_F1); + Clobber(r_F2); + Clobber(r_F3); + Clobber(r_F4); + Clobber(r_F5); + Clobber(r_F6); + Clobber(r_F7); + Clobber(r_F8); + Clobber(r_F9); + Clobber(r_F10); + Clobber(r_F11); + Clobber(r_F12); + Clobber(r_F13); + Clobber(r_F14); + Clobber(r_F15); +} + +RegLocation MipsMir2Lir::GetReturnWideAlt() +{ + UNIMPLEMENTED(FATAL) << "No GetReturnWideAlt for MIPS"; + RegLocation res = LocCReturnWide(); + return res; +} + +RegLocation MipsMir2Lir::GetReturnAlt() +{ + UNIMPLEMENTED(FATAL) << "No GetReturnAlt for MIPS"; + RegLocation res = LocCReturn(); + return res; +} + +MipsMir2Lir::RegisterInfo* MipsMir2Lir::GetRegInfo(int reg) +{ + return MIPS_FPREG(reg) ? ®_pool_->FPRegs[reg & MIPS_FP_REG_MASK] + : ®_pool_->core_regs[reg]; +} + +/* To be used when explicitly managing register use */ +void MipsMir2Lir::LockCallTemps() +{ + LockTemp(rMIPS_ARG0); + LockTemp(rMIPS_ARG1); + LockTemp(rMIPS_ARG2); + LockTemp(rMIPS_ARG3); +} + +/* To be used when explicitly managing register use */ +void MipsMir2Lir::FreeCallTemps() +{ + FreeTemp(rMIPS_ARG0); + FreeTemp(rMIPS_ARG1); + FreeTemp(rMIPS_ARG2); + FreeTemp(rMIPS_ARG3); +} + +void MipsMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) +{ +#if ANDROID_SMP != 0 + NewLIR1(kMipsSync, 0 /* Only stype currently supported */); +#endif +} + +/* + * Alloc a pair of core registers, or a double. Low reg in low byte, + * high reg in next byte. + */ +int MipsMir2Lir::AllocTypedTempPair(bool fp_hint, + int reg_class) +{ + int high_reg; + int low_reg; + int res = 0; + + if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) { + low_reg = AllocTempDouble(); + high_reg = low_reg + 1; + res = (low_reg & 0xff) | ((high_reg & 0xff) << 8); + return res; + } + + low_reg = AllocTemp(); + high_reg = AllocTemp(); + res = (low_reg & 0xff) | ((high_reg & 0xff) << 8); + return res; +} + +int MipsMir2Lir::AllocTypedTemp(bool fp_hint, int reg_class) +{ + if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) +{ + return AllocTempFloat(); +} + return AllocTemp(); +} + +void MipsMir2Lir::CompilerInitializeRegAlloc() +{ + int num_regs = sizeof(core_regs)/sizeof(*core_regs); + int num_reserved = sizeof(ReservedRegs)/sizeof(*ReservedRegs); + int num_temps = sizeof(core_temps)/sizeof(*core_temps); + int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs); + int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps); + reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true, + ArenaAllocator::kAllocRegAlloc)); + reg_pool_->num_core_regs = num_regs; + reg_pool_->core_regs = static_cast<RegisterInfo*> + (arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true, + ArenaAllocator::kAllocRegAlloc)); + reg_pool_->num_fp_regs = num_fp_regs; + reg_pool_->FPRegs = static_cast<RegisterInfo*> + (arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true, + ArenaAllocator::kAllocRegAlloc)); + CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs); + CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs); + // Keep special registers from being allocated + for (int i = 0; i < num_reserved; i++) { + if (NO_SUSPEND && (ReservedRegs[i] == rMIPS_SUSPEND)) { + //To measure cost of suspend check + continue; + } + MarkInUse(ReservedRegs[i]); + } + // Mark temp regs - all others not in use can be used for promotion + for (int i = 0; i < num_temps; i++) { + MarkTemp(core_temps[i]); + } + for (int i = 0; i < num_fp_temps; i++) { + MarkTemp(fp_temps[i]); + } +} + +void MipsMir2Lir::FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free) +{ + if ((rl_free.low_reg != rl_keep.low_reg) && (rl_free.low_reg != rl_keep.high_reg) && + (rl_free.high_reg != rl_keep.low_reg) && (rl_free.high_reg != rl_keep.high_reg)) { + // No overlap, free both + FreeTemp(rl_free.low_reg); + FreeTemp(rl_free.high_reg); + } +} +/* + * In the Arm code a it is typical to use the link register + * to hold the target address. However, for Mips we must + * ensure that all branch instructions can be restarted if + * there is a trap in the shadow. Allocate a temp register. + */ +int MipsMir2Lir::LoadHelper(int offset) +{ + LoadWordDisp(rMIPS_SELF, offset, r_T9); + return r_T9; +} + +void MipsMir2Lir::SpillCoreRegs() +{ + if (num_core_spills_ == 0) { + return; + } + uint32_t mask = core_spill_mask_; + int offset = num_core_spills_ * 4; + OpRegImm(kOpSub, rMIPS_SP, offset); + for (int reg = 0; mask; mask >>= 1, reg++) { + if (mask & 0x1) { + offset -= 4; + StoreWordDisp(rMIPS_SP, offset, reg); + } + } +} + +void MipsMir2Lir::UnSpillCoreRegs() +{ + if (num_core_spills_ == 0) { + return; + } + uint32_t mask = core_spill_mask_; + int offset = frame_size_; + for (int reg = 0; mask; mask >>= 1, reg++) { + if (mask & 0x1) { + offset -= 4; + LoadWordDisp(rMIPS_SP, offset, reg); + } + } + OpRegImm(kOpAdd, rMIPS_SP, frame_size_); +} + +bool MipsMir2Lir::IsUnconditionalBranch(LIR* lir) +{ + return (lir->opcode == kMipsB); +} + +MipsMir2Lir::MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena) + : Mir2Lir(cu, mir_graph, arena) { + for (int i = 0; i < kMipsLast; i++) { + if (MipsMir2Lir::EncodingMap[i].opcode != i) { + LOG(FATAL) << "Encoding order for " << MipsMir2Lir::EncodingMap[i].name + << " is wrong: expecting " << i << ", seeing " + << static_cast<int>(MipsMir2Lir::EncodingMap[i].opcode); + } + } +} + +Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, + ArenaAllocator* const arena) { + return new MipsMir2Lir(cu, mir_graph, arena); +} + +uint64_t MipsMir2Lir::GetTargetInstFlags(int opcode) +{ + return MipsMir2Lir::EncodingMap[opcode].flags; +} + +const char* MipsMir2Lir::GetTargetInstName(int opcode) +{ + return MipsMir2Lir::EncodingMap[opcode].name; +} + +const char* MipsMir2Lir::GetTargetInstFmt(int opcode) +{ + return MipsMir2Lir::EncodingMap[opcode].fmt; +} + +} // namespace art diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc new file mode 100644 index 0000000000..8daafc8d96 --- /dev/null +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -0,0 +1,700 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "codegen_mips.h" +#include "dex/quick/mir_to_lir-inl.h" +#include "mips_lir.h" + +namespace art { + +/* This file contains codegen for the MIPS32 ISA. */ +LIR* MipsMir2Lir::OpFpRegCopy(int r_dest, int r_src) +{ + int opcode; + /* must be both DOUBLE or both not DOUBLE */ + DCHECK_EQ(MIPS_DOUBLEREG(r_dest),MIPS_DOUBLEREG(r_src)); + if (MIPS_DOUBLEREG(r_dest)) { + opcode = kMipsFmovd; + } else { + if (MIPS_SINGLEREG(r_dest)) { + if (MIPS_SINGLEREG(r_src)) { + opcode = kMipsFmovs; + } else { + /* note the operands are swapped for the mtc1 instr */ + int t_opnd = r_src; + r_src = r_dest; + r_dest = t_opnd; + opcode = kMipsMtc1; + } + } else { + DCHECK(MIPS_SINGLEREG(r_src)); + opcode = kMipsMfc1; + } + } + LIR* res = RawLIR(current_dalvik_offset_, opcode, r_src, r_dest); + if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { + res->flags.is_nop = true; + } + return res; +} + +bool MipsMir2Lir::InexpensiveConstantInt(int32_t value) +{ + return ((value == 0) || IsUint(16, value) || ((value < 0) && (value >= -32768))); +} + +bool MipsMir2Lir::InexpensiveConstantFloat(int32_t value) +{ + return false; // TUNING +} + +bool MipsMir2Lir::InexpensiveConstantLong(int64_t value) +{ + return false; // TUNING +} + +bool MipsMir2Lir::InexpensiveConstantDouble(int64_t value) +{ + return false; // TUNING +} + +/* + * Load a immediate using a shortcut if possible; otherwise + * grab from the per-translation literal pool. If target is + * a high register, build constant into a low register and copy. + * + * No additional register clobbering operation performed. Use this version when + * 1) r_dest is freshly returned from AllocTemp or + * 2) The codegen is under fixed register usage + */ +LIR* MipsMir2Lir::LoadConstantNoClobber(int r_dest, int value) +{ + LIR *res; + + int r_dest_save = r_dest; + int is_fp_reg = MIPS_FPREG(r_dest); + if (is_fp_reg) { + DCHECK(MIPS_SINGLEREG(r_dest)); + r_dest = AllocTemp(); + } + + /* See if the value can be constructed cheaply */ + if (value == 0) { + res = NewLIR2(kMipsMove, r_dest, r_ZERO); + } else if ((value > 0) && (value <= 65535)) { + res = NewLIR3(kMipsOri, r_dest, r_ZERO, value); + } else if ((value < 0) && (value >= -32768)) { + res = NewLIR3(kMipsAddiu, r_dest, r_ZERO, value); + } else { + res = NewLIR2(kMipsLui, r_dest, value>>16); + if (value & 0xffff) + NewLIR3(kMipsOri, r_dest, r_dest, value); + } + + if (is_fp_reg) { + NewLIR2(kMipsMtc1, r_dest, r_dest_save); + FreeTemp(r_dest); + } + + return res; +} + +LIR* MipsMir2Lir::OpUnconditionalBranch(LIR* target) +{ + LIR* res = NewLIR1(kMipsB, 0 /* offset to be patched during assembly*/ ); + res->target = target; + return res; +} + +LIR* MipsMir2Lir::OpReg(OpKind op, int r_dest_src) +{ + MipsOpCode opcode = kMipsNop; + switch (op) { + case kOpBlx: + opcode = kMipsJalr; + break; + case kOpBx: + return NewLIR1(kMipsJr, r_dest_src); + break; + default: + LOG(FATAL) << "Bad case in OpReg"; + } + return NewLIR2(opcode, r_RA, r_dest_src); +} + +LIR* MipsMir2Lir::OpRegImm(OpKind op, int r_dest_src1, + int value) +{ + LIR *res; + bool neg = (value < 0); + int abs_value = (neg) ? -value : value; + bool short_form = (abs_value & 0xff) == abs_value; + MipsOpCode opcode = kMipsNop; + switch (op) { + case kOpAdd: + return OpRegRegImm(op, r_dest_src1, r_dest_src1, value); + break; + case kOpSub: + return OpRegRegImm(op, r_dest_src1, r_dest_src1, value); + break; + default: + LOG(FATAL) << "Bad case in OpRegImm"; + break; + } + if (short_form) + res = NewLIR2(opcode, r_dest_src1, abs_value); + else { + int r_scratch = AllocTemp(); + res = LoadConstant(r_scratch, value); + if (op == kOpCmp) + NewLIR2(opcode, r_dest_src1, r_scratch); + else + NewLIR3(opcode, r_dest_src1, r_dest_src1, r_scratch); + } + return res; +} + +LIR* MipsMir2Lir::OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2) +{ + MipsOpCode opcode = kMipsNop; + switch (op) { + case kOpAdd: + opcode = kMipsAddu; + break; + case kOpSub: + opcode = kMipsSubu; + break; + case kOpAnd: + opcode = kMipsAnd; + break; + case kOpMul: + opcode = kMipsMul; + break; + case kOpOr: + opcode = kMipsOr; + break; + case kOpXor: + opcode = kMipsXor; + break; + case kOpLsl: + opcode = kMipsSllv; + break; + case kOpLsr: + opcode = kMipsSrlv; + break; + case kOpAsr: + opcode = kMipsSrav; + break; + case kOpAdc: + case kOpSbc: + LOG(FATAL) << "No carry bit on MIPS"; + break; + default: + LOG(FATAL) << "bad case in OpRegRegReg"; + break; + } + return NewLIR3(opcode, r_dest, r_src1, r_src2); +} + +LIR* MipsMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) +{ + LIR *res; + MipsOpCode opcode = kMipsNop; + bool short_form = true; + + switch (op) { + case kOpAdd: + if (IS_SIMM16(value)) { + opcode = kMipsAddiu; + } + else { + short_form = false; + opcode = kMipsAddu; + } + break; + case kOpSub: + if (IS_SIMM16((-value))) { + value = -value; + opcode = kMipsAddiu; + } + else { + short_form = false; + opcode = kMipsSubu; + } + break; + case kOpLsl: + DCHECK(value >= 0 && value <= 31); + opcode = kMipsSll; + break; + case kOpLsr: + DCHECK(value >= 0 && value <= 31); + opcode = kMipsSrl; + break; + case kOpAsr: + DCHECK(value >= 0 && value <= 31); + opcode = kMipsSra; + break; + case kOpAnd: + if (IS_UIMM16((value))) { + opcode = kMipsAndi; + } + else { + short_form = false; + opcode = kMipsAnd; + } + break; + case kOpOr: + if (IS_UIMM16((value))) { + opcode = kMipsOri; + } + else { + short_form = false; + opcode = kMipsOr; + } + break; + case kOpXor: + if (IS_UIMM16((value))) { + opcode = kMipsXori; + } + else { + short_form = false; + opcode = kMipsXor; + } + break; + case kOpMul: + short_form = false; + opcode = kMipsMul; + break; + default: + LOG(FATAL) << "Bad case in OpRegRegImm"; + break; + } + + if (short_form) + res = NewLIR3(opcode, r_dest, r_src1, value); + else { + if (r_dest != r_src1) { + res = LoadConstant(r_dest, value); + NewLIR3(opcode, r_dest, r_src1, r_dest); + } else { + int r_scratch = AllocTemp(); + res = LoadConstant(r_scratch, value); + NewLIR3(opcode, r_dest, r_src1, r_scratch); + } + } + return res; +} + +LIR* MipsMir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2) +{ + MipsOpCode opcode = kMipsNop; + LIR *res; + switch (op) { + case kOpMov: + opcode = kMipsMove; + break; + case kOpMvn: + return NewLIR3(kMipsNor, r_dest_src1, r_src2, r_ZERO); + case kOpNeg: + return NewLIR3(kMipsSubu, r_dest_src1, r_ZERO, r_src2); + case kOpAdd: + case kOpAnd: + case kOpMul: + case kOpOr: + case kOpSub: + case kOpXor: + return OpRegRegReg(op, r_dest_src1, r_dest_src1, r_src2); + case kOp2Byte: +#if __mips_isa_rev>=2 + res = NewLIR2(kMipsSeb, r_dest_src1, r_src2); +#else + res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 24); + OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 24); +#endif + return res; + case kOp2Short: +#if __mips_isa_rev>=2 + res = NewLIR2(kMipsSeh, r_dest_src1, r_src2); +#else + res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 16); + OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 16); +#endif + return res; + case kOp2Char: + return NewLIR3(kMipsAndi, r_dest_src1, r_src2, 0xFFFF); + default: + LOG(FATAL) << "Bad case in OpRegReg"; + break; + } + return NewLIR2(opcode, r_dest_src1, r_src2); +} + +LIR* MipsMir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value) +{ + LIR *res; + res = LoadConstantNoClobber(r_dest_lo, Low32Bits(value)); + LoadConstantNoClobber(r_dest_hi, High32Bits(value)); + return res; +} + +/* Load value from base + scaled index. */ +LIR* MipsMir2Lir::LoadBaseIndexed(int rBase, int r_index, int r_dest, + int scale, OpSize size) +{ + LIR *first = NULL; + LIR *res; + MipsOpCode opcode = kMipsNop; + int t_reg = AllocTemp(); + + if (MIPS_FPREG(r_dest)) { + DCHECK(MIPS_SINGLEREG(r_dest)); + DCHECK((size == kWord) || (size == kSingle)); + size = kSingle; + } else { + if (size == kSingle) + size = kWord; + } + + if (!scale) { + first = NewLIR3(kMipsAddu, t_reg , rBase, r_index); + } else { + first = OpRegRegImm(kOpLsl, t_reg, r_index, scale); + NewLIR3(kMipsAddu, t_reg , rBase, t_reg); + } + + switch (size) { + case kSingle: + opcode = kMipsFlwc1; + break; + case kWord: + opcode = kMipsLw; + break; + case kUnsignedHalf: + opcode = kMipsLhu; + break; + case kSignedHalf: + opcode = kMipsLh; + break; + case kUnsignedByte: + opcode = kMipsLbu; + break; + case kSignedByte: + opcode = kMipsLb; + break; + default: + LOG(FATAL) << "Bad case in LoadBaseIndexed"; + } + + res = NewLIR3(opcode, r_dest, 0, t_reg); + FreeTemp(t_reg); + return (first) ? first : res; +} + +/* store value base base + scaled index. */ +LIR* MipsMir2Lir::StoreBaseIndexed(int rBase, int r_index, int r_src, + int scale, OpSize size) +{ + LIR *first = NULL; + MipsOpCode opcode = kMipsNop; + int r_new_index = r_index; + int t_reg = AllocTemp(); + + if (MIPS_FPREG(r_src)) { + DCHECK(MIPS_SINGLEREG(r_src)); + DCHECK((size == kWord) || (size == kSingle)); + size = kSingle; + } else { + if (size == kSingle) + size = kWord; + } + + if (!scale) { + first = NewLIR3(kMipsAddu, t_reg , rBase, r_index); + } else { + first = OpRegRegImm(kOpLsl, t_reg, r_index, scale); + NewLIR3(kMipsAddu, t_reg , rBase, t_reg); + } + + switch (size) { + case kSingle: + opcode = kMipsFswc1; + break; + case kWord: + opcode = kMipsSw; + break; + case kUnsignedHalf: + case kSignedHalf: + opcode = kMipsSh; + break; + case kUnsignedByte: + case kSignedByte: + opcode = kMipsSb; + break; + default: + LOG(FATAL) << "Bad case in StoreBaseIndexed"; + } + NewLIR3(opcode, r_src, 0, t_reg); + FreeTemp(r_new_index); + return first; +} + +LIR* MipsMir2Lir::LoadBaseDispBody(int rBase, int displacement, int r_dest, + int r_dest_hi, OpSize size, int s_reg) +/* + * Load value from base + displacement. Optionally perform null check + * on base (which must have an associated s_reg and MIR). If not + * performing null check, incoming MIR can be null. IMPORTANT: this + * code must not allocate any new temps. If a new register is needed + * and base and dest are the same, spill some other register to + * rlp and then restore. + */ +{ + LIR *res; + LIR *load = NULL; + LIR *load2 = NULL; + MipsOpCode opcode = kMipsNop; + bool short_form = IS_SIMM16(displacement); + bool pair = false; + + switch (size) { + case kLong: + case kDouble: + pair = true; + opcode = kMipsLw; + if (MIPS_FPREG(r_dest)) { + opcode = kMipsFlwc1; + if (MIPS_DOUBLEREG(r_dest)) { + r_dest = r_dest - MIPS_FP_DOUBLE; + } else { + DCHECK(MIPS_FPREG(r_dest_hi)); + DCHECK(r_dest == (r_dest_hi - 1)); + } + r_dest_hi = r_dest + 1; + } + short_form = IS_SIMM16_2WORD(displacement); + DCHECK_EQ((displacement & 0x3), 0); + break; + case kWord: + case kSingle: + opcode = kMipsLw; + if (MIPS_FPREG(r_dest)) { + opcode = kMipsFlwc1; + DCHECK(MIPS_SINGLEREG(r_dest)); + } + DCHECK_EQ((displacement & 0x3), 0); + break; + case kUnsignedHalf: + opcode = kMipsLhu; + DCHECK_EQ((displacement & 0x1), 0); + break; + case kSignedHalf: + opcode = kMipsLh; + DCHECK_EQ((displacement & 0x1), 0); + break; + case kUnsignedByte: + opcode = kMipsLbu; + break; + case kSignedByte: + opcode = kMipsLb; + break; + default: + LOG(FATAL) << "Bad case in LoadBaseIndexedBody"; + } + + if (short_form) { + if (!pair) { + load = res = NewLIR3(opcode, r_dest, displacement, rBase); + } else { + load = res = NewLIR3(opcode, r_dest, + displacement + LOWORD_OFFSET, rBase); + load2 = NewLIR3(opcode, r_dest_hi, + displacement + HIWORD_OFFSET, rBase); + } + } else { + if (pair) { + int r_tmp = AllocFreeTemp(); + res = OpRegRegImm(kOpAdd, r_tmp, rBase, displacement); + load = NewLIR3(opcode, r_dest, LOWORD_OFFSET, r_tmp); + load2 = NewLIR3(opcode, r_dest_hi, HIWORD_OFFSET, r_tmp); + FreeTemp(r_tmp); + } else { + int r_tmp = (rBase == r_dest) ? AllocFreeTemp() : r_dest; + res = OpRegRegImm(kOpAdd, r_tmp, rBase, displacement); + load = NewLIR3(opcode, r_dest, 0, r_tmp); + if (r_tmp != r_dest) + FreeTemp(r_tmp); + } + } + + if (rBase == rMIPS_SP) { + AnnotateDalvikRegAccess(load, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2, + true /* is_load */, pair /* is64bit */); + if (pair) { + AnnotateDalvikRegAccess(load2, (displacement + HIWORD_OFFSET) >> 2, + true /* is_load */, pair /* is64bit */); + } + } + return load; +} + +LIR* MipsMir2Lir::LoadBaseDisp(int rBase, int displacement, int r_dest, + OpSize size, int s_reg) +{ + return LoadBaseDispBody(rBase, displacement, r_dest, -1, + size, s_reg); +} + +LIR* MipsMir2Lir::LoadBaseDispWide(int rBase, int displacement, + int r_dest_lo, int r_dest_hi, int s_reg) +{ + return LoadBaseDispBody(rBase, displacement, r_dest_lo, r_dest_hi, kLong, s_reg); +} + +LIR* MipsMir2Lir::StoreBaseDispBody(int rBase, int displacement, + int r_src, int r_src_hi, OpSize size) +{ + LIR *res; + LIR *store = NULL; + LIR *store2 = NULL; + MipsOpCode opcode = kMipsNop; + bool short_form = IS_SIMM16(displacement); + bool pair = false; + + switch (size) { + case kLong: + case kDouble: + pair = true; + opcode = kMipsSw; + if (MIPS_FPREG(r_src)) { + opcode = kMipsFswc1; + if (MIPS_DOUBLEREG(r_src)) { + r_src = r_src - MIPS_FP_DOUBLE; + } else { + DCHECK(MIPS_FPREG(r_src_hi)); + DCHECK_EQ(r_src, (r_src_hi - 1)); + } + r_src_hi = r_src + 1; + } + short_form = IS_SIMM16_2WORD(displacement); + DCHECK_EQ((displacement & 0x3), 0); + break; + case kWord: + case kSingle: + opcode = kMipsSw; + if (MIPS_FPREG(r_src)) { + opcode = kMipsFswc1; + DCHECK(MIPS_SINGLEREG(r_src)); + } + DCHECK_EQ((displacement & 0x3), 0); + break; + case kUnsignedHalf: + case kSignedHalf: + opcode = kMipsSh; + DCHECK_EQ((displacement & 0x1), 0); + break; + case kUnsignedByte: + case kSignedByte: + opcode = kMipsSb; + break; + default: + LOG(FATAL) << "Bad case in StoreBaseIndexedBody"; + } + + if (short_form) { + if (!pair) { + store = res = NewLIR3(opcode, r_src, displacement, rBase); + } else { + store = res = NewLIR3(opcode, r_src, displacement + LOWORD_OFFSET, + rBase); + store2 = NewLIR3(opcode, r_src_hi, displacement + HIWORD_OFFSET, + rBase); + } + } else { + int r_scratch = AllocTemp(); + res = OpRegRegImm(kOpAdd, r_scratch, rBase, displacement); + if (!pair) { + store = NewLIR3(opcode, r_src, 0, r_scratch); + } else { + store = NewLIR3(opcode, r_src, LOWORD_OFFSET, r_scratch); + store2 = NewLIR3(opcode, r_src_hi, HIWORD_OFFSET, r_scratch); + } + FreeTemp(r_scratch); + } + + if (rBase == rMIPS_SP) { + AnnotateDalvikRegAccess(store, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2, + false /* is_load */, pair /* is64bit */); + if (pair) { + AnnotateDalvikRegAccess(store2, (displacement + HIWORD_OFFSET) >> 2, + false /* is_load */, pair /* is64bit */); + } + } + + return res; +} + +LIR* MipsMir2Lir::StoreBaseDisp(int rBase, int displacement, int r_src, + OpSize size) +{ + return StoreBaseDispBody(rBase, displacement, r_src, -1, size); +} + +LIR* MipsMir2Lir::StoreBaseDispWide(int rBase, int displacement, + int r_src_lo, int r_src_hi) +{ + return StoreBaseDispBody(rBase, displacement, r_src_lo, r_src_hi, kLong); +} + +LIR* MipsMir2Lir::OpThreadMem(OpKind op, int thread_offset) +{ + LOG(FATAL) << "Unexpected use of OpThreadMem for MIPS"; + return NULL; +} + +LIR* MipsMir2Lir::OpMem(OpKind op, int rBase, int disp) +{ + LOG(FATAL) << "Unexpected use of OpMem for MIPS"; + return NULL; +} + +LIR* MipsMir2Lir::StoreBaseIndexedDisp( int rBase, int r_index, int scale, int displacement, + int r_src, int r_src_hi, OpSize size, int s_reg) +{ + LOG(FATAL) << "Unexpected use of StoreBaseIndexedDisp for MIPS"; + return NULL; +} + +LIR* MipsMir2Lir::OpRegMem(OpKind op, int r_dest, int rBase, + int offset) +{ + LOG(FATAL) << "Unexpected use of OpRegMem for MIPS"; + return NULL; +} + +LIR* MipsMir2Lir::LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, + int r_dest, int r_dest_hi, OpSize size, int s_reg) +{ + LOG(FATAL) << "Unexpected use of LoadBaseIndexedDisp for MIPS"; + return NULL; +} + +LIR* MipsMir2Lir::OpCondBranch(ConditionCode cc, LIR* target) +{ + LOG(FATAL) << "Unexpected use of OpCondBranch for MIPS"; + return NULL; +} + +} // namespace art |