/* * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ #ifndef CPU_ARM_VM_ASSEMBLER_ARM_HPP #define CPU_ARM_VM_ASSEMBLER_ARM_HPP #include "utilities/macros.hpp" enum AsmCondition { eq, ne, cs, cc, mi, pl, vs, vc, hi, ls, ge, lt, gt, le, al, nv, number_of_conditions, // alternative names hs = cs, lo = cc }; enum AsmShift { lsl, lsr, asr, ror }; #ifdef AARCH64 enum AsmExtendOp { ex_uxtb, ex_uxth, ex_uxtw, ex_uxtx, ex_sxtb, ex_sxth, ex_sxtw, ex_sxtx, ex_lsl = ex_uxtx }; #endif enum AsmOffset { #ifdef AARCH64 basic_offset = 0b00, pre_indexed = 0b11, post_indexed = 0b01 #else basic_offset = 1 << 24, pre_indexed = 1 << 24 | 1 << 21, post_indexed = 0 #endif }; #ifndef AARCH64 enum AsmWriteback { no_writeback, writeback }; enum AsmOffsetOp { sub_offset = 0, add_offset = 1 }; #endif // ARM Addressing Modes 2 and 3 - Load and store class Address VALUE_OBJ_CLASS_SPEC { private: Register _base; Register _index; int _disp; AsmOffset _mode; RelocationHolder _rspec; int _shift_imm; #ifdef AARCH64 AsmExtendOp _extend; #else AsmShift _shift; AsmOffsetOp _offset_op; static inline int abs(int x) { return x < 0 ? -x : x; } static inline int up (int x) { return x < 0 ? 0 : 1; } #endif #ifdef AARCH64 static const AsmExtendOp LSL = ex_lsl; #else static const AsmShift LSL = lsl; #endif public: Address() : _base(noreg) {} Address(Register rn, int offset = 0, AsmOffset mode = basic_offset) { _base = rn; _index = noreg; _disp = offset; _mode = mode; _shift_imm = 0; #ifdef AARCH64 _extend = ex_lsl; #else _shift = lsl; _offset_op = add_offset; #endif } #ifdef ASSERT Address(Register rn, ByteSize offset, AsmOffset mode = basic_offset) { _base = rn; _index = noreg; _disp = in_bytes(offset); _mode = mode; _shift_imm = 0; #ifdef AARCH64 _extend = ex_lsl; #else _shift = lsl; _offset_op = add_offset; #endif } #endif #ifdef AARCH64 Address(Register rn, Register rm, AsmExtendOp extend = ex_lsl, int shift_imm = 0) { assert ((extend == ex_uxtw) || (extend == ex_lsl) || (extend == ex_sxtw) || (extend == ex_sxtx), "invalid extend for address mode"); assert ((0 <= shift_imm) && (shift_imm <= 4), "shift amount is out of range"); _base = rn; _index = rm; _disp = 0; _mode = basic_offset; _extend = extend; _shift_imm = shift_imm; } #else Address(Register rn, Register rm, AsmShift shift = lsl, int shift_imm = 0, AsmOffset mode = basic_offset, AsmOffsetOp offset_op = add_offset) { _base = rn; _index = rm; _disp = 0; _shift = shift; _shift_imm = shift_imm; _mode = mode; _offset_op = offset_op; } Address(Register rn, RegisterOrConstant offset, AsmShift shift = lsl, int shift_imm = 0) { _base = rn; if (offset.is_constant()) { _index = noreg; { int off = (int) offset.as_constant(); if (shift_imm != 0) { assert(shift == lsl,"shift not yet encoded"); off = off << shift_imm; } _disp = off; } _shift = lsl; _shift_imm = 0; } else { _index = offset.as_register(); _disp = 0; _shift = shift; _shift_imm = shift_imm; } _mode = basic_offset; _offset_op = add_offset; } #endif // AARCH64 // [base + index * wordSize] static Address indexed_ptr(Register base, Register index) { return Address(base, index, LSL, LogBytesPerWord); } // [base + index * BytesPerInt] static Address indexed_32(Register base, Register index) { return Address(base, index, LSL, LogBytesPerInt); } // [base + index * BytesPerHeapOop] static Address indexed_oop(Register base, Register index) { return Address(base, index, LSL, LogBytesPerHeapOop); } Address plus_disp(int disp) const { assert((disp == 0) || (_index == noreg),"can't apply an offset to a register indexed address"); Address a = (*this); a._disp += disp; return a; } Address rebase(Register new_base) const { Address a = (*this); a._base = new_base; return a; } #ifdef AARCH64 int encoding_simd() const { assert(_index != SP, "encoding constraint"); assert(_disp == 0 || _mode == post_indexed, "encoding constraint"); assert(_index == noreg || _mode == basic_offset, "encoding constraint"); assert(_mode == basic_offset || _mode == post_indexed, "encoding constraint"); assert(_extend == ex_lsl, "encoding constraint"); int index; if (_index == noreg) { if (_mode == post_indexed) index = 0b100 << 5 | 31; else index = 0; } else { index = 0b100 << 5 | _index->encoding(); } return index << 16 | _base->encoding_with_sp() << 5; } #else /* !AARCH64 */ int encoding2() const { assert(_mode == basic_offset || _base != PC, "unpredictable instruction"); if (_index == noreg) { assert(-4096 < _disp && _disp < 4096, "encoding constraint"); return _mode | up(_disp) << 23 | _base->encoding() << 16 | abs(_disp); } else { assert(_index != PC && (_mode == basic_offset || _index != _base), "unpredictable instruction"); assert(_disp == 0 && (_shift_imm >> 5) == 0, "encoding constraint"); return 1 << 25 | _offset_op << 23 | _mode | _base->encoding() << 16 | _shift_imm << 7 | _shift << 5 | _index->encoding(); } } int encoding3() const { assert(_mode == basic_offset || _base != PC, "unpredictable instruction"); if (_index == noreg) { assert(-256 < _disp && _disp < 256, "encoding constraint"); return _mode | up(_disp) << 23 | 1 << 22 | _base->encoding() << 16 | (abs(_disp) & 0xf0) << 4 | abs(_disp) & 0x0f; } else { assert(_index != PC && (_mode == basic_offset || _index != _base), "unpredictable instruction"); assert(_disp == 0 && _shift == lsl && _shift_imm == 0, "encoding constraint"); return _mode | _offset_op << 23 | _base->encoding() << 16 | _index->encoding(); } } int encoding_ex() const { assert(_index == noreg && _disp == 0 && _mode == basic_offset && _base != PC, "encoding constraint"); return _base->encoding() << 16; } int encoding_vfp() const { assert(_index == noreg && _mode == basic_offset, "encoding constraint"); assert(-1024 < _disp && _disp < 1024 && (_disp & 3) == 0, "encoding constraint"); return _base->encoding() << 16 | up(_disp) << 23 | abs(_disp) >> 2; } int encoding_simd() const { assert(_base != PC, "encoding constraint"); assert(_index != PC && _index != SP, "encoding constraint"); assert(_disp == 0, "encoding constraint"); assert(_shift == 0, "encoding constraint"); assert(_index == noreg || _mode == basic_offset, "encoding constraint"); assert(_mode == basic_offset || _mode == post_indexed, "encoding constraint"); int index; if (_index == noreg) { if (_mode == post_indexed) index = 13; else index = 15; } else { index = _index->encoding(); } return _base->encoding() << 16 | index; } #endif // !AARCH64 Register base() const { return _base; } Register index() const { return _index; } int disp() const { return _disp; } AsmOffset mode() const { return _mode; } int shift_imm() const { return _shift_imm; } #ifdef AARCH64 AsmExtendOp extend() const { return _extend; } #else AsmShift shift() const { return _shift; } AsmOffsetOp offset_op() const { return _offset_op; } #endif bool uses(Register reg) const { return _base == reg || _index == reg; } const relocInfo::relocType rtype() { return _rspec.type(); } const RelocationHolder& rspec() { return _rspec; } // Convert the raw encoding form into the form expected by the // constructor for Address. static Address make_raw(int base, int index, int scale, int disp, relocInfo::relocType disp_reloc); }; #ifdef COMPILER2 class VFP VALUE_OBJ_CLASS_SPEC { // Helper classes to detect whether a floating point constant can be // encoded in a fconstd or fconsts instruction // The conversion from the imm8, 8 bit constant, to the floating // point value encoding is done with either: // for single precision: imm8<7>:NOT(imm8<6>):Replicate(imm8<6>,5):imm8<5:0>:Zeros(19) // or // for double precision: imm8<7>:NOT(imm8<6>):Replicate(imm8<6>,8):imm8<5:0>:Zeros(48) private: class fpnum { public: virtual unsigned int f_hi4() const = 0; virtual bool f_lo_is_null() const = 0; virtual int e() const = 0; virtual unsigned int s() const = 0; inline bool can_be_imm8() const { return e() >= -3 && e() <= 4 && f_lo_is_null(); } inline unsigned char imm8() const { int v = (s() << 7) | (((e() - 1) & 0x7) << 4) | f_hi4(); assert((v >> 8) == 0, "overflow"); return v; } }; public: class float_num : public fpnum { public: float_num(float v) { _num.val = v; } virtual unsigned int f_hi4() const { return (_num.bits << 9) >> (19+9); } virtual bool f_lo_is_null() const { return (_num.bits & ((1 << 19) - 1)) == 0; } virtual int e() const { return ((_num.bits << 1) >> (23+1)) - 127; } virtual unsigned int s() const { return _num.bits >> 31; } private: union { float val; unsigned int bits; } _num; }; class double_num : public fpnum { public: double_num(double v) { _num.val = v; } virtual unsigned int f_hi4() const { return (_num.bits << 12) >> (48+12); } virtual bool f_lo_is_null() const { return (_num.bits & ((1LL << 48) - 1)) == 0; } virtual int e() const { return ((_num.bits << 1) >> (52+1)) - 1023; } virtual unsigned int s() const { return _num.bits >> 63; } private: union { double val; unsigned long long bits; } _num; }; }; #endif #ifdef AARCH64 #include "assembler_arm_64.hpp" #else #include "assembler_arm_32.hpp" #endif #endif // CPU_ARM_VM_ASSEMBLER_ARM_HPP