dep: Add vixl (AArch32/64 assembler)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,855 @@
|
||||
// Copyright 2016, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "aarch32/constants-aarch32.h"
|
||||
#include "utils-vixl.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch32 {
|
||||
|
||||
// Start of generated code.
|
||||
const char* ToCString(InstructionType type) {
|
||||
switch (type) {
|
||||
case kAdc:
|
||||
return "adc";
|
||||
case kAdcs:
|
||||
return "adcs";
|
||||
case kAdd:
|
||||
return "add";
|
||||
case kAdds:
|
||||
return "adds";
|
||||
case kAddw:
|
||||
return "addw";
|
||||
case kAdr:
|
||||
return "adr";
|
||||
case kAnd:
|
||||
return "and";
|
||||
case kAnds:
|
||||
return "ands";
|
||||
case kAsr:
|
||||
return "asr";
|
||||
case kAsrs:
|
||||
return "asrs";
|
||||
case kB:
|
||||
return "b";
|
||||
case kBfc:
|
||||
return "bfc";
|
||||
case kBfi:
|
||||
return "bfi";
|
||||
case kBic:
|
||||
return "bic";
|
||||
case kBics:
|
||||
return "bics";
|
||||
case kBkpt:
|
||||
return "bkpt";
|
||||
case kBl:
|
||||
return "bl";
|
||||
case kBlx:
|
||||
return "blx";
|
||||
case kBx:
|
||||
return "bx";
|
||||
case kBxj:
|
||||
return "bxj";
|
||||
case kCbnz:
|
||||
return "cbnz";
|
||||
case kCbz:
|
||||
return "cbz";
|
||||
case kClrex:
|
||||
return "clrex";
|
||||
case kClz:
|
||||
return "clz";
|
||||
case kCmn:
|
||||
return "cmn";
|
||||
case kCmp:
|
||||
return "cmp";
|
||||
case kCrc32b:
|
||||
return "crc32b";
|
||||
case kCrc32cb:
|
||||
return "crc32cb";
|
||||
case kCrc32ch:
|
||||
return "crc32ch";
|
||||
case kCrc32cw:
|
||||
return "crc32cw";
|
||||
case kCrc32h:
|
||||
return "crc32h";
|
||||
case kCrc32w:
|
||||
return "crc32w";
|
||||
case kDmb:
|
||||
return "dmb";
|
||||
case kDsb:
|
||||
return "dsb";
|
||||
case kEor:
|
||||
return "eor";
|
||||
case kEors:
|
||||
return "eors";
|
||||
case kFldmdbx:
|
||||
return "fldmdbx";
|
||||
case kFldmiax:
|
||||
return "fldmiax";
|
||||
case kFstmdbx:
|
||||
return "fstmdbx";
|
||||
case kFstmiax:
|
||||
return "fstmiax";
|
||||
case kHlt:
|
||||
return "hlt";
|
||||
case kHvc:
|
||||
return "hvc";
|
||||
case kIsb:
|
||||
return "isb";
|
||||
case kIt:
|
||||
return "it";
|
||||
case kLda:
|
||||
return "lda";
|
||||
case kLdab:
|
||||
return "ldab";
|
||||
case kLdaex:
|
||||
return "ldaex";
|
||||
case kLdaexb:
|
||||
return "ldaexb";
|
||||
case kLdaexd:
|
||||
return "ldaexd";
|
||||
case kLdaexh:
|
||||
return "ldaexh";
|
||||
case kLdah:
|
||||
return "ldah";
|
||||
case kLdm:
|
||||
return "ldm";
|
||||
case kLdmda:
|
||||
return "ldmda";
|
||||
case kLdmdb:
|
||||
return "ldmdb";
|
||||
case kLdmea:
|
||||
return "ldmea";
|
||||
case kLdmed:
|
||||
return "ldmed";
|
||||
case kLdmfa:
|
||||
return "ldmfa";
|
||||
case kLdmfd:
|
||||
return "ldmfd";
|
||||
case kLdmib:
|
||||
return "ldmib";
|
||||
case kLdr:
|
||||
return "ldr";
|
||||
case kLdrb:
|
||||
return "ldrb";
|
||||
case kLdrd:
|
||||
return "ldrd";
|
||||
case kLdrex:
|
||||
return "ldrex";
|
||||
case kLdrexb:
|
||||
return "ldrexb";
|
||||
case kLdrexd:
|
||||
return "ldrexd";
|
||||
case kLdrexh:
|
||||
return "ldrexh";
|
||||
case kLdrh:
|
||||
return "ldrh";
|
||||
case kLdrsb:
|
||||
return "ldrsb";
|
||||
case kLdrsh:
|
||||
return "ldrsh";
|
||||
case kLsl:
|
||||
return "lsl";
|
||||
case kLsls:
|
||||
return "lsls";
|
||||
case kLsr:
|
||||
return "lsr";
|
||||
case kLsrs:
|
||||
return "lsrs";
|
||||
case kMla:
|
||||
return "mla";
|
||||
case kMlas:
|
||||
return "mlas";
|
||||
case kMls:
|
||||
return "mls";
|
||||
case kMov:
|
||||
return "mov";
|
||||
case kMovs:
|
||||
return "movs";
|
||||
case kMovt:
|
||||
return "movt";
|
||||
case kMovw:
|
||||
return "movw";
|
||||
case kMrs:
|
||||
return "mrs";
|
||||
case kMsr:
|
||||
return "msr";
|
||||
case kMul:
|
||||
return "mul";
|
||||
case kMuls:
|
||||
return "muls";
|
||||
case kMvn:
|
||||
return "mvn";
|
||||
case kMvns:
|
||||
return "mvns";
|
||||
case kNop:
|
||||
return "nop";
|
||||
case kOrn:
|
||||
return "orn";
|
||||
case kOrns:
|
||||
return "orns";
|
||||
case kOrr:
|
||||
return "orr";
|
||||
case kOrrs:
|
||||
return "orrs";
|
||||
case kPkhbt:
|
||||
return "pkhbt";
|
||||
case kPkhtb:
|
||||
return "pkhtb";
|
||||
case kPld:
|
||||
return "pld";
|
||||
case kPldw:
|
||||
return "pldw";
|
||||
case kPli:
|
||||
return "pli";
|
||||
case kPop:
|
||||
return "pop";
|
||||
case kPush:
|
||||
return "push";
|
||||
case kQadd:
|
||||
return "qadd";
|
||||
case kQadd16:
|
||||
return "qadd16";
|
||||
case kQadd8:
|
||||
return "qadd8";
|
||||
case kQasx:
|
||||
return "qasx";
|
||||
case kQdadd:
|
||||
return "qdadd";
|
||||
case kQdsub:
|
||||
return "qdsub";
|
||||
case kQsax:
|
||||
return "qsax";
|
||||
case kQsub:
|
||||
return "qsub";
|
||||
case kQsub16:
|
||||
return "qsub16";
|
||||
case kQsub8:
|
||||
return "qsub8";
|
||||
case kRbit:
|
||||
return "rbit";
|
||||
case kRev:
|
||||
return "rev";
|
||||
case kRev16:
|
||||
return "rev16";
|
||||
case kRevsh:
|
||||
return "revsh";
|
||||
case kRor:
|
||||
return "ror";
|
||||
case kRors:
|
||||
return "rors";
|
||||
case kRrx:
|
||||
return "rrx";
|
||||
case kRrxs:
|
||||
return "rrxs";
|
||||
case kRsb:
|
||||
return "rsb";
|
||||
case kRsbs:
|
||||
return "rsbs";
|
||||
case kRsc:
|
||||
return "rsc";
|
||||
case kRscs:
|
||||
return "rscs";
|
||||
case kSadd16:
|
||||
return "sadd16";
|
||||
case kSadd8:
|
||||
return "sadd8";
|
||||
case kSasx:
|
||||
return "sasx";
|
||||
case kSbc:
|
||||
return "sbc";
|
||||
case kSbcs:
|
||||
return "sbcs";
|
||||
case kSbfx:
|
||||
return "sbfx";
|
||||
case kSdiv:
|
||||
return "sdiv";
|
||||
case kSel:
|
||||
return "sel";
|
||||
case kShadd16:
|
||||
return "shadd16";
|
||||
case kShadd8:
|
||||
return "shadd8";
|
||||
case kShasx:
|
||||
return "shasx";
|
||||
case kShsax:
|
||||
return "shsax";
|
||||
case kShsub16:
|
||||
return "shsub16";
|
||||
case kShsub8:
|
||||
return "shsub8";
|
||||
case kSmlabb:
|
||||
return "smlabb";
|
||||
case kSmlabt:
|
||||
return "smlabt";
|
||||
case kSmlad:
|
||||
return "smlad";
|
||||
case kSmladx:
|
||||
return "smladx";
|
||||
case kSmlal:
|
||||
return "smlal";
|
||||
case kSmlalbb:
|
||||
return "smlalbb";
|
||||
case kSmlalbt:
|
||||
return "smlalbt";
|
||||
case kSmlald:
|
||||
return "smlald";
|
||||
case kSmlaldx:
|
||||
return "smlaldx";
|
||||
case kSmlals:
|
||||
return "smlals";
|
||||
case kSmlaltb:
|
||||
return "smlaltb";
|
||||
case kSmlaltt:
|
||||
return "smlaltt";
|
||||
case kSmlatb:
|
||||
return "smlatb";
|
||||
case kSmlatt:
|
||||
return "smlatt";
|
||||
case kSmlawb:
|
||||
return "smlawb";
|
||||
case kSmlawt:
|
||||
return "smlawt";
|
||||
case kSmlsd:
|
||||
return "smlsd";
|
||||
case kSmlsdx:
|
||||
return "smlsdx";
|
||||
case kSmlsld:
|
||||
return "smlsld";
|
||||
case kSmlsldx:
|
||||
return "smlsldx";
|
||||
case kSmmla:
|
||||
return "smmla";
|
||||
case kSmmlar:
|
||||
return "smmlar";
|
||||
case kSmmls:
|
||||
return "smmls";
|
||||
case kSmmlsr:
|
||||
return "smmlsr";
|
||||
case kSmmul:
|
||||
return "smmul";
|
||||
case kSmmulr:
|
||||
return "smmulr";
|
||||
case kSmuad:
|
||||
return "smuad";
|
||||
case kSmuadx:
|
||||
return "smuadx";
|
||||
case kSmulbb:
|
||||
return "smulbb";
|
||||
case kSmulbt:
|
||||
return "smulbt";
|
||||
case kSmull:
|
||||
return "smull";
|
||||
case kSmulls:
|
||||
return "smulls";
|
||||
case kSmultb:
|
||||
return "smultb";
|
||||
case kSmultt:
|
||||
return "smultt";
|
||||
case kSmulwb:
|
||||
return "smulwb";
|
||||
case kSmulwt:
|
||||
return "smulwt";
|
||||
case kSmusd:
|
||||
return "smusd";
|
||||
case kSmusdx:
|
||||
return "smusdx";
|
||||
case kSsat:
|
||||
return "ssat";
|
||||
case kSsat16:
|
||||
return "ssat16";
|
||||
case kSsax:
|
||||
return "ssax";
|
||||
case kSsub16:
|
||||
return "ssub16";
|
||||
case kSsub8:
|
||||
return "ssub8";
|
||||
case kStl:
|
||||
return "stl";
|
||||
case kStlb:
|
||||
return "stlb";
|
||||
case kStlex:
|
||||
return "stlex";
|
||||
case kStlexb:
|
||||
return "stlexb";
|
||||
case kStlexd:
|
||||
return "stlexd";
|
||||
case kStlexh:
|
||||
return "stlexh";
|
||||
case kStlh:
|
||||
return "stlh";
|
||||
case kStm:
|
||||
return "stm";
|
||||
case kStmda:
|
||||
return "stmda";
|
||||
case kStmdb:
|
||||
return "stmdb";
|
||||
case kStmea:
|
||||
return "stmea";
|
||||
case kStmed:
|
||||
return "stmed";
|
||||
case kStmfa:
|
||||
return "stmfa";
|
||||
case kStmfd:
|
||||
return "stmfd";
|
||||
case kStmib:
|
||||
return "stmib";
|
||||
case kStr:
|
||||
return "str";
|
||||
case kStrb:
|
||||
return "strb";
|
||||
case kStrd:
|
||||
return "strd";
|
||||
case kStrex:
|
||||
return "strex";
|
||||
case kStrexb:
|
||||
return "strexb";
|
||||
case kStrexd:
|
||||
return "strexd";
|
||||
case kStrexh:
|
||||
return "strexh";
|
||||
case kStrh:
|
||||
return "strh";
|
||||
case kSub:
|
||||
return "sub";
|
||||
case kSubs:
|
||||
return "subs";
|
||||
case kSubw:
|
||||
return "subw";
|
||||
case kSvc:
|
||||
return "svc";
|
||||
case kSxtab:
|
||||
return "sxtab";
|
||||
case kSxtab16:
|
||||
return "sxtab16";
|
||||
case kSxtah:
|
||||
return "sxtah";
|
||||
case kSxtb:
|
||||
return "sxtb";
|
||||
case kSxtb16:
|
||||
return "sxtb16";
|
||||
case kSxth:
|
||||
return "sxth";
|
||||
case kTbb:
|
||||
return "tbb";
|
||||
case kTbh:
|
||||
return "tbh";
|
||||
case kTeq:
|
||||
return "teq";
|
||||
case kTst:
|
||||
return "tst";
|
||||
case kUadd16:
|
||||
return "uadd16";
|
||||
case kUadd8:
|
||||
return "uadd8";
|
||||
case kUasx:
|
||||
return "uasx";
|
||||
case kUbfx:
|
||||
return "ubfx";
|
||||
case kUdf:
|
||||
return "udf";
|
||||
case kUdiv:
|
||||
return "udiv";
|
||||
case kUhadd16:
|
||||
return "uhadd16";
|
||||
case kUhadd8:
|
||||
return "uhadd8";
|
||||
case kUhasx:
|
||||
return "uhasx";
|
||||
case kUhsax:
|
||||
return "uhsax";
|
||||
case kUhsub16:
|
||||
return "uhsub16";
|
||||
case kUhsub8:
|
||||
return "uhsub8";
|
||||
case kUmaal:
|
||||
return "umaal";
|
||||
case kUmlal:
|
||||
return "umlal";
|
||||
case kUmlals:
|
||||
return "umlals";
|
||||
case kUmull:
|
||||
return "umull";
|
||||
case kUmulls:
|
||||
return "umulls";
|
||||
case kUqadd16:
|
||||
return "uqadd16";
|
||||
case kUqadd8:
|
||||
return "uqadd8";
|
||||
case kUqasx:
|
||||
return "uqasx";
|
||||
case kUqsax:
|
||||
return "uqsax";
|
||||
case kUqsub16:
|
||||
return "uqsub16";
|
||||
case kUqsub8:
|
||||
return "uqsub8";
|
||||
case kUsad8:
|
||||
return "usad8";
|
||||
case kUsada8:
|
||||
return "usada8";
|
||||
case kUsat:
|
||||
return "usat";
|
||||
case kUsat16:
|
||||
return "usat16";
|
||||
case kUsax:
|
||||
return "usax";
|
||||
case kUsub16:
|
||||
return "usub16";
|
||||
case kUsub8:
|
||||
return "usub8";
|
||||
case kUxtab:
|
||||
return "uxtab";
|
||||
case kUxtab16:
|
||||
return "uxtab16";
|
||||
case kUxtah:
|
||||
return "uxtah";
|
||||
case kUxtb:
|
||||
return "uxtb";
|
||||
case kUxtb16:
|
||||
return "uxtb16";
|
||||
case kUxth:
|
||||
return "uxth";
|
||||
case kVaba:
|
||||
return "vaba";
|
||||
case kVabal:
|
||||
return "vabal";
|
||||
case kVabd:
|
||||
return "vabd";
|
||||
case kVabdl:
|
||||
return "vabdl";
|
||||
case kVabs:
|
||||
return "vabs";
|
||||
case kVacge:
|
||||
return "vacge";
|
||||
case kVacgt:
|
||||
return "vacgt";
|
||||
case kVacle:
|
||||
return "vacle";
|
||||
case kVaclt:
|
||||
return "vaclt";
|
||||
case kVadd:
|
||||
return "vadd";
|
||||
case kVaddhn:
|
||||
return "vaddhn";
|
||||
case kVaddl:
|
||||
return "vaddl";
|
||||
case kVaddw:
|
||||
return "vaddw";
|
||||
case kVand:
|
||||
return "vand";
|
||||
case kVbic:
|
||||
return "vbic";
|
||||
case kVbif:
|
||||
return "vbif";
|
||||
case kVbit:
|
||||
return "vbit";
|
||||
case kVbsl:
|
||||
return "vbsl";
|
||||
case kVceq:
|
||||
return "vceq";
|
||||
case kVcge:
|
||||
return "vcge";
|
||||
case kVcgt:
|
||||
return "vcgt";
|
||||
case kVcle:
|
||||
return "vcle";
|
||||
case kVcls:
|
||||
return "vcls";
|
||||
case kVclt:
|
||||
return "vclt";
|
||||
case kVclz:
|
||||
return "vclz";
|
||||
case kVcmp:
|
||||
return "vcmp";
|
||||
case kVcmpe:
|
||||
return "vcmpe";
|
||||
case kVcnt:
|
||||
return "vcnt";
|
||||
case kVcvt:
|
||||
return "vcvt";
|
||||
case kVcvta:
|
||||
return "vcvta";
|
||||
case kVcvtb:
|
||||
return "vcvtb";
|
||||
case kVcvtm:
|
||||
return "vcvtm";
|
||||
case kVcvtn:
|
||||
return "vcvtn";
|
||||
case kVcvtp:
|
||||
return "vcvtp";
|
||||
case kVcvtr:
|
||||
return "vcvtr";
|
||||
case kVcvtt:
|
||||
return "vcvtt";
|
||||
case kVdiv:
|
||||
return "vdiv";
|
||||
case kVdup:
|
||||
return "vdup";
|
||||
case kVeor:
|
||||
return "veor";
|
||||
case kVext:
|
||||
return "vext";
|
||||
case kVfma:
|
||||
return "vfma";
|
||||
case kVfms:
|
||||
return "vfms";
|
||||
case kVfnma:
|
||||
return "vfnma";
|
||||
case kVfnms:
|
||||
return "vfnms";
|
||||
case kVhadd:
|
||||
return "vhadd";
|
||||
case kVhsub:
|
||||
return "vhsub";
|
||||
case kVld1:
|
||||
return "vld1";
|
||||
case kVld2:
|
||||
return "vld2";
|
||||
case kVld3:
|
||||
return "vld3";
|
||||
case kVld4:
|
||||
return "vld4";
|
||||
case kVldm:
|
||||
return "vldm";
|
||||
case kVldmdb:
|
||||
return "vldmdb";
|
||||
case kVldmia:
|
||||
return "vldmia";
|
||||
case kVldr:
|
||||
return "vldr";
|
||||
case kVmax:
|
||||
return "vmax";
|
||||
case kVmaxnm:
|
||||
return "vmaxnm";
|
||||
case kVmin:
|
||||
return "vmin";
|
||||
case kVminnm:
|
||||
return "vminnm";
|
||||
case kVmla:
|
||||
return "vmla";
|
||||
case kVmlal:
|
||||
return "vmlal";
|
||||
case kVmls:
|
||||
return "vmls";
|
||||
case kVmlsl:
|
||||
return "vmlsl";
|
||||
case kVmov:
|
||||
return "vmov";
|
||||
case kVmovl:
|
||||
return "vmovl";
|
||||
case kVmovn:
|
||||
return "vmovn";
|
||||
case kVmrs:
|
||||
return "vmrs";
|
||||
case kVmsr:
|
||||
return "vmsr";
|
||||
case kVmul:
|
||||
return "vmul";
|
||||
case kVmull:
|
||||
return "vmull";
|
||||
case kVmvn:
|
||||
return "vmvn";
|
||||
case kVneg:
|
||||
return "vneg";
|
||||
case kVnmla:
|
||||
return "vnmla";
|
||||
case kVnmls:
|
||||
return "vnmls";
|
||||
case kVnmul:
|
||||
return "vnmul";
|
||||
case kVorn:
|
||||
return "vorn";
|
||||
case kVorr:
|
||||
return "vorr";
|
||||
case kVpadal:
|
||||
return "vpadal";
|
||||
case kVpadd:
|
||||
return "vpadd";
|
||||
case kVpaddl:
|
||||
return "vpaddl";
|
||||
case kVpmax:
|
||||
return "vpmax";
|
||||
case kVpmin:
|
||||
return "vpmin";
|
||||
case kVpop:
|
||||
return "vpop";
|
||||
case kVpush:
|
||||
return "vpush";
|
||||
case kVqabs:
|
||||
return "vqabs";
|
||||
case kVqadd:
|
||||
return "vqadd";
|
||||
case kVqdmlal:
|
||||
return "vqdmlal";
|
||||
case kVqdmlsl:
|
||||
return "vqdmlsl";
|
||||
case kVqdmulh:
|
||||
return "vqdmulh";
|
||||
case kVqdmull:
|
||||
return "vqdmull";
|
||||
case kVqmovn:
|
||||
return "vqmovn";
|
||||
case kVqmovun:
|
||||
return "vqmovun";
|
||||
case kVqneg:
|
||||
return "vqneg";
|
||||
case kVqrdmulh:
|
||||
return "vqrdmulh";
|
||||
case kVqrshl:
|
||||
return "vqrshl";
|
||||
case kVqrshrn:
|
||||
return "vqrshrn";
|
||||
case kVqrshrun:
|
||||
return "vqrshrun";
|
||||
case kVqshl:
|
||||
return "vqshl";
|
||||
case kVqshlu:
|
||||
return "vqshlu";
|
||||
case kVqshrn:
|
||||
return "vqshrn";
|
||||
case kVqshrun:
|
||||
return "vqshrun";
|
||||
case kVqsub:
|
||||
return "vqsub";
|
||||
case kVraddhn:
|
||||
return "vraddhn";
|
||||
case kVrecpe:
|
||||
return "vrecpe";
|
||||
case kVrecps:
|
||||
return "vrecps";
|
||||
case kVrev16:
|
||||
return "vrev16";
|
||||
case kVrev32:
|
||||
return "vrev32";
|
||||
case kVrev64:
|
||||
return "vrev64";
|
||||
case kVrhadd:
|
||||
return "vrhadd";
|
||||
case kVrinta:
|
||||
return "vrinta";
|
||||
case kVrintm:
|
||||
return "vrintm";
|
||||
case kVrintn:
|
||||
return "vrintn";
|
||||
case kVrintp:
|
||||
return "vrintp";
|
||||
case kVrintr:
|
||||
return "vrintr";
|
||||
case kVrintx:
|
||||
return "vrintx";
|
||||
case kVrintz:
|
||||
return "vrintz";
|
||||
case kVrshl:
|
||||
return "vrshl";
|
||||
case kVrshr:
|
||||
return "vrshr";
|
||||
case kVrshrn:
|
||||
return "vrshrn";
|
||||
case kVrsqrte:
|
||||
return "vrsqrte";
|
||||
case kVrsqrts:
|
||||
return "vrsqrts";
|
||||
case kVrsra:
|
||||
return "vrsra";
|
||||
case kVrsubhn:
|
||||
return "vrsubhn";
|
||||
case kVseleq:
|
||||
return "vseleq";
|
||||
case kVselge:
|
||||
return "vselge";
|
||||
case kVselgt:
|
||||
return "vselgt";
|
||||
case kVselvs:
|
||||
return "vselvs";
|
||||
case kVshl:
|
||||
return "vshl";
|
||||
case kVshll:
|
||||
return "vshll";
|
||||
case kVshr:
|
||||
return "vshr";
|
||||
case kVshrn:
|
||||
return "vshrn";
|
||||
case kVsli:
|
||||
return "vsli";
|
||||
case kVsqrt:
|
||||
return "vsqrt";
|
||||
case kVsra:
|
||||
return "vsra";
|
||||
case kVsri:
|
||||
return "vsri";
|
||||
case kVst1:
|
||||
return "vst1";
|
||||
case kVst2:
|
||||
return "vst2";
|
||||
case kVst3:
|
||||
return "vst3";
|
||||
case kVst4:
|
||||
return "vst4";
|
||||
case kVstm:
|
||||
return "vstm";
|
||||
case kVstmdb:
|
||||
return "vstmdb";
|
||||
case kVstmia:
|
||||
return "vstmia";
|
||||
case kVstr:
|
||||
return "vstr";
|
||||
case kVsub:
|
||||
return "vsub";
|
||||
case kVsubhn:
|
||||
return "vsubhn";
|
||||
case kVsubl:
|
||||
return "vsubl";
|
||||
case kVsubw:
|
||||
return "vsubw";
|
||||
case kVswp:
|
||||
return "vswp";
|
||||
case kVtbl:
|
||||
return "vtbl";
|
||||
case kVtbx:
|
||||
return "vtbx";
|
||||
case kVtrn:
|
||||
return "vtrn";
|
||||
case kVtst:
|
||||
return "vtst";
|
||||
case kVuzp:
|
||||
return "vuzp";
|
||||
case kVzip:
|
||||
return "vzip";
|
||||
case kYield:
|
||||
return "yield";
|
||||
case kUndefInstructionType:
|
||||
VIXL_UNREACHABLE();
|
||||
return "";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return "";
|
||||
} // NOLINT(readability/fn_size)
|
||||
// End of generated code.
|
||||
|
||||
} // namespace aarch32
|
||||
} // namespace vixl
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,742 @@
|
||||
// Copyright 2017, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
extern "C" {
|
||||
#include <stdint.h>
|
||||
}
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "utils-vixl.h"
|
||||
#include "aarch32/constants-aarch32.h"
|
||||
#include "aarch32/instructions-aarch32.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch32 {
|
||||
|
||||
|
||||
bool Shift::IsValidAmount(uint32_t amount) const {
|
||||
switch (GetType()) {
|
||||
case LSL:
|
||||
return amount <= 31;
|
||||
case ROR:
|
||||
return (amount > 0) && (amount <= 31);
|
||||
case LSR:
|
||||
case ASR:
|
||||
return (amount > 0) && (amount <= 32);
|
||||
case RRX:
|
||||
return amount == 0;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Register reg) {
|
||||
switch (reg.GetCode()) {
|
||||
case 12:
|
||||
return os << "ip";
|
||||
case 13:
|
||||
return os << "sp";
|
||||
case 14:
|
||||
return os << "lr";
|
||||
case 15:
|
||||
return os << "pc";
|
||||
default:
|
||||
return os << "r" << reg.GetCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SRegister VRegister::S() const {
|
||||
VIXL_ASSERT(GetType() == kSRegister);
|
||||
return SRegister(GetCode());
|
||||
}
|
||||
|
||||
|
||||
DRegister VRegister::D() const {
|
||||
VIXL_ASSERT(GetType() == kDRegister);
|
||||
return DRegister(GetCode());
|
||||
}
|
||||
|
||||
|
||||
QRegister VRegister::Q() const {
|
||||
VIXL_ASSERT(GetType() == kQRegister);
|
||||
return QRegister(GetCode());
|
||||
}
|
||||
|
||||
|
||||
Register RegisterList::GetFirstAvailableRegister() const {
|
||||
for (uint32_t i = 0; i < kNumberOfRegisters; i++) {
|
||||
if (((list_ >> i) & 1) != 0) return Register(i);
|
||||
}
|
||||
return Register();
|
||||
}
|
||||
|
||||
|
||||
std::ostream& PrintRegisterList(std::ostream& os, // NOLINT(runtime/references)
|
||||
uint32_t list) {
|
||||
os << "{";
|
||||
bool first = true;
|
||||
int code = 0;
|
||||
while (list != 0) {
|
||||
if ((list & 1) != 0) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
os << ",";
|
||||
}
|
||||
os << Register(code);
|
||||
}
|
||||
list >>= 1;
|
||||
code++;
|
||||
}
|
||||
os << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, RegisterList registers) {
|
||||
return PrintRegisterList(os, registers.GetList());
|
||||
}
|
||||
|
||||
|
||||
QRegister VRegisterList::GetFirstAvailableQRegister() const {
|
||||
for (uint32_t i = 0; i < kNumberOfQRegisters; i++) {
|
||||
if (((list_ >> (i * 4)) & 0xf) == 0xf) return QRegister(i);
|
||||
}
|
||||
return QRegister();
|
||||
}
|
||||
|
||||
|
||||
DRegister VRegisterList::GetFirstAvailableDRegister() const {
|
||||
for (uint32_t i = 0; i < kMaxNumberOfDRegisters; i++) {
|
||||
if (((list_ >> (i * 2)) & 0x3) == 0x3) return DRegister(i);
|
||||
}
|
||||
return DRegister();
|
||||
}
|
||||
|
||||
|
||||
SRegister VRegisterList::GetFirstAvailableSRegister() const {
|
||||
for (uint32_t i = 0; i < kNumberOfSRegisters; i++) {
|
||||
if (((list_ >> i) & 0x1) != 0) return SRegister(i);
|
||||
}
|
||||
return SRegister();
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, SRegisterList reglist) {
|
||||
SRegister first = reglist.GetFirstSRegister();
|
||||
SRegister last = reglist.GetLastSRegister();
|
||||
if (first.Is(last))
|
||||
os << "{" << first << "}";
|
||||
else
|
||||
os << "{" << first << "-" << last << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, DRegisterList reglist) {
|
||||
DRegister first = reglist.GetFirstDRegister();
|
||||
DRegister last = reglist.GetLastDRegister();
|
||||
if (first.Is(last))
|
||||
os << "{" << first << "}";
|
||||
else
|
||||
os << "{" << first << "-" << last << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, NeonRegisterList nreglist) {
|
||||
DRegister first = nreglist.GetFirstDRegister();
|
||||
int increment = nreglist.IsSingleSpaced() ? 1 : 2;
|
||||
int count =
|
||||
nreglist.GetLastDRegister().GetCode() - first.GetCode() + increment;
|
||||
if (count < 0) count += kMaxNumberOfDRegisters;
|
||||
os << "{";
|
||||
bool first_displayed = false;
|
||||
for (;;) {
|
||||
if (first_displayed) {
|
||||
os << ",";
|
||||
} else {
|
||||
first_displayed = true;
|
||||
}
|
||||
os << first;
|
||||
if (nreglist.IsTransferOneLane()) {
|
||||
os << "[" << nreglist.GetTransferLane() << "]";
|
||||
} else if (nreglist.IsTransferAllLanes()) {
|
||||
os << "[]";
|
||||
}
|
||||
count -= increment;
|
||||
if (count <= 0) break;
|
||||
unsigned next = first.GetCode() + increment;
|
||||
if (next >= kMaxNumberOfDRegisters) next -= kMaxNumberOfDRegisters;
|
||||
first = DRegister(next);
|
||||
}
|
||||
os << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
const char* SpecialRegister::GetName() const {
|
||||
switch (reg_) {
|
||||
case APSR:
|
||||
return "APSR";
|
||||
case SPSR:
|
||||
return "SPSR";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return "??";
|
||||
}
|
||||
|
||||
|
||||
const char* MaskedSpecialRegister::GetName() const {
|
||||
switch (reg_) {
|
||||
case APSR_nzcvq:
|
||||
return "APSR_nzcvq";
|
||||
case APSR_g:
|
||||
return "APSR_g";
|
||||
case APSR_nzcvqg:
|
||||
return "APSR_nzcvqg";
|
||||
case CPSR_c:
|
||||
return "CPSR_c";
|
||||
case CPSR_x:
|
||||
return "CPSR_x";
|
||||
case CPSR_xc:
|
||||
return "CPSR_xc";
|
||||
case CPSR_sc:
|
||||
return "CPSR_sc";
|
||||
case CPSR_sx:
|
||||
return "CPSR_sx";
|
||||
case CPSR_sxc:
|
||||
return "CPSR_sxc";
|
||||
case CPSR_fc:
|
||||
return "CPSR_fc";
|
||||
case CPSR_fx:
|
||||
return "CPSR_fx";
|
||||
case CPSR_fxc:
|
||||
return "CPSR_fxc";
|
||||
case CPSR_fsc:
|
||||
return "CPSR_fsc";
|
||||
case CPSR_fsx:
|
||||
return "CPSR_fsx";
|
||||
case CPSR_fsxc:
|
||||
return "CPSR_fsxc";
|
||||
case SPSR_c:
|
||||
return "SPSR_c";
|
||||
case SPSR_x:
|
||||
return "SPSR_x";
|
||||
case SPSR_xc:
|
||||
return "SPSR_xc";
|
||||
case SPSR_s:
|
||||
return "SPSR_s";
|
||||
case SPSR_sc:
|
||||
return "SPSR_sc";
|
||||
case SPSR_sx:
|
||||
return "SPSR_sx";
|
||||
case SPSR_sxc:
|
||||
return "SPSR_sxc";
|
||||
case SPSR_f:
|
||||
return "SPSR_f";
|
||||
case SPSR_fc:
|
||||
return "SPSR_fc";
|
||||
case SPSR_fx:
|
||||
return "SPSR_fx";
|
||||
case SPSR_fxc:
|
||||
return "SPSR_fxc";
|
||||
case SPSR_fs:
|
||||
return "SPSR_fs";
|
||||
case SPSR_fsc:
|
||||
return "SPSR_fsc";
|
||||
case SPSR_fsx:
|
||||
return "SPSR_fsx";
|
||||
case SPSR_fsxc:
|
||||
return "SPSR_fsxc";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return "??";
|
||||
}
|
||||
|
||||
|
||||
const char* BankedRegister::GetName() const {
|
||||
switch (reg_) {
|
||||
case R8_usr:
|
||||
return "R8_usr";
|
||||
case R9_usr:
|
||||
return "R9_usr";
|
||||
case R10_usr:
|
||||
return "R10_usr";
|
||||
case R11_usr:
|
||||
return "R11_usr";
|
||||
case R12_usr:
|
||||
return "R12_usr";
|
||||
case SP_usr:
|
||||
return "SP_usr";
|
||||
case LR_usr:
|
||||
return "LR_usr";
|
||||
case R8_fiq:
|
||||
return "R8_fiq";
|
||||
case R9_fiq:
|
||||
return "R9_fiq";
|
||||
case R10_fiq:
|
||||
return "R10_fiq";
|
||||
case R11_fiq:
|
||||
return "R11_fiq";
|
||||
case R12_fiq:
|
||||
return "R12_fiq";
|
||||
case SP_fiq:
|
||||
return "SP_fiq";
|
||||
case LR_fiq:
|
||||
return "LR_fiq";
|
||||
case LR_irq:
|
||||
return "LR_irq";
|
||||
case SP_irq:
|
||||
return "SP_irq";
|
||||
case LR_svc:
|
||||
return "LR_svc";
|
||||
case SP_svc:
|
||||
return "SP_svc";
|
||||
case LR_abt:
|
||||
return "LR_abt";
|
||||
case SP_abt:
|
||||
return "SP_abt";
|
||||
case LR_und:
|
||||
return "LR_und";
|
||||
case SP_und:
|
||||
return "SP_und";
|
||||
case LR_mon:
|
||||
return "LR_mon";
|
||||
case SP_mon:
|
||||
return "SP_mon";
|
||||
case ELR_hyp:
|
||||
return "ELR_hyp";
|
||||
case SP_hyp:
|
||||
return "SP_hyp";
|
||||
case SPSR_fiq:
|
||||
return "SPSR_fiq";
|
||||
case SPSR_irq:
|
||||
return "SPSR_irq";
|
||||
case SPSR_svc:
|
||||
return "SPSR_svc";
|
||||
case SPSR_abt:
|
||||
return "SPSR_abt";
|
||||
case SPSR_und:
|
||||
return "SPSR_und";
|
||||
case SPSR_mon:
|
||||
return "SPSR_mon";
|
||||
case SPSR_hyp:
|
||||
return "SPSR_hyp";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return "??";
|
||||
}
|
||||
|
||||
const char* SpecialFPRegister::GetName() const {
|
||||
switch (reg_) {
|
||||
case FPSID:
|
||||
return "FPSID";
|
||||
case FPSCR:
|
||||
return "FPSCR";
|
||||
case MVFR2:
|
||||
return "MVFR2";
|
||||
case MVFR1:
|
||||
return "MVFR1";
|
||||
case MVFR0:
|
||||
return "MVFR0";
|
||||
case FPEXC:
|
||||
return "FPEXC";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return "??";
|
||||
}
|
||||
|
||||
|
||||
const char* Condition::GetName() const {
|
||||
switch (condition_) {
|
||||
case eq:
|
||||
return "eq";
|
||||
case ne:
|
||||
return "ne";
|
||||
case cs:
|
||||
return "cs";
|
||||
case cc:
|
||||
return "cc";
|
||||
case mi:
|
||||
return "mi";
|
||||
case pl:
|
||||
return "pl";
|
||||
case vs:
|
||||
return "vs";
|
||||
case vc:
|
||||
return "vc";
|
||||
case hi:
|
||||
return "hi";
|
||||
case ls:
|
||||
return "ls";
|
||||
case ge:
|
||||
return "ge";
|
||||
case lt:
|
||||
return "lt";
|
||||
case gt:
|
||||
return "gt";
|
||||
case le:
|
||||
return "le";
|
||||
case al:
|
||||
return "";
|
||||
case Condition::kNone:
|
||||
return "";
|
||||
}
|
||||
return "<und>";
|
||||
}
|
||||
|
||||
|
||||
const char* Shift::GetName() const {
|
||||
switch (shift_) {
|
||||
case LSL:
|
||||
return "lsl";
|
||||
case LSR:
|
||||
return "lsr";
|
||||
case ASR:
|
||||
return "asr";
|
||||
case ROR:
|
||||
return "ror";
|
||||
case RRX:
|
||||
return "rrx";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return "??";
|
||||
}
|
||||
|
||||
|
||||
const char* EncodingSize::GetName() const {
|
||||
switch (size_) {
|
||||
case Best:
|
||||
case Narrow:
|
||||
return "";
|
||||
case Wide:
|
||||
return ".w";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return "??";
|
||||
}
|
||||
|
||||
|
||||
const char* DataType::GetName() const {
|
||||
switch (value_) {
|
||||
case kDataTypeValueInvalid:
|
||||
return ".??";
|
||||
case kDataTypeValueNone:
|
||||
return "";
|
||||
case S8:
|
||||
return ".s8";
|
||||
case S16:
|
||||
return ".s16";
|
||||
case S32:
|
||||
return ".s32";
|
||||
case S64:
|
||||
return ".s64";
|
||||
case U8:
|
||||
return ".u8";
|
||||
case U16:
|
||||
return ".u16";
|
||||
case U32:
|
||||
return ".u32";
|
||||
case U64:
|
||||
return ".u64";
|
||||
case F16:
|
||||
return ".f16";
|
||||
case F32:
|
||||
return ".f32";
|
||||
case F64:
|
||||
return ".f64";
|
||||
case I8:
|
||||
return ".i8";
|
||||
case I16:
|
||||
return ".i16";
|
||||
case I32:
|
||||
return ".i32";
|
||||
case I64:
|
||||
return ".i64";
|
||||
case P8:
|
||||
return ".p8";
|
||||
case P64:
|
||||
return ".p64";
|
||||
case Untyped8:
|
||||
return ".8";
|
||||
case Untyped16:
|
||||
return ".16";
|
||||
case Untyped32:
|
||||
return ".32";
|
||||
case Untyped64:
|
||||
return ".64";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return ".??";
|
||||
}
|
||||
|
||||
|
||||
const char* MemoryBarrier::GetName() const {
|
||||
switch (type_) {
|
||||
case OSHLD:
|
||||
return "oshld";
|
||||
case OSHST:
|
||||
return "oshst";
|
||||
case OSH:
|
||||
return "osh";
|
||||
case NSHLD:
|
||||
return "nshld";
|
||||
case NSHST:
|
||||
return "nshst";
|
||||
case NSH:
|
||||
return "nsh";
|
||||
case ISHLD:
|
||||
return "ishld";
|
||||
case ISHST:
|
||||
return "ishst";
|
||||
case ISH:
|
||||
return "ish";
|
||||
case LD:
|
||||
return "ld";
|
||||
case ST:
|
||||
return "st";
|
||||
case SY:
|
||||
return "sy";
|
||||
}
|
||||
switch (static_cast<int>(type_)) {
|
||||
case 0:
|
||||
return "#0x0";
|
||||
case 4:
|
||||
return "#0x4";
|
||||
case 8:
|
||||
return "#0x8";
|
||||
case 0xc:
|
||||
return "#0xc";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return "??";
|
||||
}
|
||||
|
||||
|
||||
const char* InterruptFlags::GetName() const {
|
||||
switch (type_) {
|
||||
case F:
|
||||
return "f";
|
||||
case I:
|
||||
return "i";
|
||||
case IF:
|
||||
return "if";
|
||||
case A:
|
||||
return "a";
|
||||
case AF:
|
||||
return "af";
|
||||
case AI:
|
||||
return "ai";
|
||||
case AIF:
|
||||
return "aif";
|
||||
}
|
||||
VIXL_ASSERT(type_ == 0);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
const char* Endianness::GetName() const {
|
||||
switch (type_) {
|
||||
case LE:
|
||||
return "le";
|
||||
case BE:
|
||||
return "be";
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return "??";
|
||||
}
|
||||
|
||||
|
||||
// Constructor used for disassembly.
|
||||
ImmediateShiftOperand::ImmediateShiftOperand(int shift_value, int amount_value)
|
||||
: Shift(shift_value) {
|
||||
switch (shift_value) {
|
||||
case LSL:
|
||||
amount_ = amount_value;
|
||||
break;
|
||||
case LSR:
|
||||
case ASR:
|
||||
amount_ = (amount_value == 0) ? 32 : amount_value;
|
||||
break;
|
||||
case ROR:
|
||||
amount_ = amount_value;
|
||||
if (amount_value == 0) SetType(RRX);
|
||||
break;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
SetType(LSL);
|
||||
amount_ = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImmediateT32::ImmediateT32(uint32_t imm) {
|
||||
// 00000000 00000000 00000000 abcdefgh
|
||||
if ((imm & ~0xff) == 0) {
|
||||
SetEncodingValue(imm);
|
||||
return;
|
||||
}
|
||||
if ((imm >> 16) == (imm & 0xffff)) {
|
||||
if ((imm & 0xff00) == 0) {
|
||||
// 00000000 abcdefgh 00000000 abcdefgh
|
||||
SetEncodingValue((imm & 0xff) | (0x1 << 8));
|
||||
return;
|
||||
}
|
||||
if ((imm & 0xff) == 0) {
|
||||
// abcdefgh 00000000 abcdefgh 00000000
|
||||
SetEncodingValue(((imm >> 8) & 0xff) | (0x2 << 8));
|
||||
return;
|
||||
}
|
||||
if (((imm >> 8) & 0xff) == (imm & 0xff)) {
|
||||
// abcdefgh abcdefgh abcdefgh abcdefgh
|
||||
SetEncodingValue((imm & 0xff) | (0x3 << 8));
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int shift = 0; shift < 24; shift++) {
|
||||
uint32_t imm8 = imm >> (24 - shift);
|
||||
uint32_t overflow = imm << (8 + shift);
|
||||
if ((imm8 <= 0xff) && ((imm8 & 0x80) != 0) && (overflow == 0)) {
|
||||
SetEncodingValue(((shift + 8) << 7) | (imm8 & 0x7F));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t ror(uint32_t x, int i) {
|
||||
VIXL_ASSERT((0 < i) && (i < 32));
|
||||
return (x >> i) | (x << (32 - i));
|
||||
}
|
||||
|
||||
|
||||
bool ImmediateT32::IsImmediateT32(uint32_t imm) {
|
||||
/* abcdefgh abcdefgh abcdefgh abcdefgh */
|
||||
if ((imm ^ ror(imm, 8)) == 0) return true;
|
||||
/* 00000000 abcdefgh 00000000 abcdefgh */
|
||||
/* abcdefgh 00000000 abcdefgh 00000000 */
|
||||
if ((imm ^ ror(imm, 16)) == 0 &&
|
||||
(((imm & 0xff00) == 0) || ((imm & 0xff) == 0)))
|
||||
return true;
|
||||
/* isolate least-significant set bit */
|
||||
uint32_t lsb = imm & -imm;
|
||||
/* if imm is less than lsb*256 then it fits, but instead we test imm/256 to
|
||||
* avoid overflow (underflow is always a successful case) */
|
||||
return ((imm >> 8) < lsb);
|
||||
}
|
||||
|
||||
|
||||
uint32_t ImmediateT32::Decode(uint32_t value) {
|
||||
uint32_t base = value & 0xff;
|
||||
switch (value >> 8) {
|
||||
case 0:
|
||||
return base;
|
||||
case 1:
|
||||
return base | (base << 16);
|
||||
case 2:
|
||||
return (base << 8) | (base << 24);
|
||||
case 3:
|
||||
return base | (base << 8) | (base << 16) | (base << 24);
|
||||
default:
|
||||
base |= 0x80;
|
||||
return base << (32 - (value >> 7));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImmediateA32::ImmediateA32(uint32_t imm) {
|
||||
// Deal with rot = 0 first to avoid undefined shift by 32.
|
||||
if (imm <= 0xff) {
|
||||
SetEncodingValue(imm);
|
||||
return;
|
||||
}
|
||||
for (int rot = 2; rot < 32; rot += 2) {
|
||||
uint32_t imm8 = (imm << rot) | (imm >> (32 - rot));
|
||||
if (imm8 <= 0xff) {
|
||||
SetEncodingValue((rot << 7) | imm8);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ImmediateA32::IsImmediateA32(uint32_t imm) {
|
||||
/* fast-out */
|
||||
if (imm < 256) return true;
|
||||
/* avoid getting confused by wrapped-around bytes (this transform has no
|
||||
* effect on pass/fail results) */
|
||||
if (imm & 0xff000000) imm = ror(imm, 16);
|
||||
/* copy odd-numbered set bits into even-numbered bits immediately below, so
|
||||
* that the least-significant set bit is always an even bit */
|
||||
imm = imm | ((imm >> 1) & 0x55555555);
|
||||
/* isolate least-significant set bit (always even) */
|
||||
uint32_t lsb = imm & -imm;
|
||||
/* if imm is less than lsb*256 then it fits, but instead we test imm/256 to
|
||||
* avoid overflow (underflow is always a successful case) */
|
||||
return ((imm >> 8) < lsb);
|
||||
}
|
||||
|
||||
|
||||
uint32_t ImmediateA32::Decode(uint32_t value) {
|
||||
int rotation = (value >> 8) * 2;
|
||||
VIXL_ASSERT(rotation >= 0);
|
||||
VIXL_ASSERT(rotation <= 30);
|
||||
value &= 0xff;
|
||||
if (rotation == 0) return value;
|
||||
return (value >> rotation) | (value << (32 - rotation));
|
||||
}
|
||||
|
||||
|
||||
uint32_t TypeEncodingValue(Shift shift) {
|
||||
return shift.IsRRX() ? kRRXEncodedValue : shift.GetValue();
|
||||
}
|
||||
|
||||
|
||||
uint32_t AmountEncodingValue(Shift shift, uint32_t amount) {
|
||||
switch (shift.GetType()) {
|
||||
case LSL:
|
||||
case ROR:
|
||||
return amount;
|
||||
case LSR:
|
||||
case ASR:
|
||||
return amount % 32;
|
||||
case RRX:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace aarch32
|
||||
} // namespace vixl
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright 2017, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "location-aarch32.h"
|
||||
|
||||
#include "assembler-aarch32.h"
|
||||
#include "macro-assembler-aarch32.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
namespace aarch32 {
|
||||
|
||||
bool Location::Needs16BitPadding(int32_t location) const {
|
||||
if (!HasForwardReferences()) return false;
|
||||
const ForwardRef& last_ref = GetLastForwardReference();
|
||||
int32_t min_location_last_ref = last_ref.GetMinLocation();
|
||||
VIXL_ASSERT(min_location_last_ref - location <= 2);
|
||||
return (min_location_last_ref > location);
|
||||
}
|
||||
|
||||
void Location::ResolveReferences(internal::AssemblerBase* assembler) {
|
||||
// Iterate over references and call EncodeLocationFor on each of them.
|
||||
for (ForwardRefListIterator it(this); !it.Done(); it.Advance()) {
|
||||
const ForwardRef& reference = *it.Current();
|
||||
VIXL_ASSERT(reference.LocationIsEncodable(location_));
|
||||
int32_t from = reference.GetLocation();
|
||||
EncodeLocationFor(assembler, from, reference.op());
|
||||
}
|
||||
forward_.clear();
|
||||
}
|
||||
|
||||
static bool Is16BitEncoding(uint16_t instr) {
|
||||
return instr < (kLowestT32_32Opcode >> 16);
|
||||
}
|
||||
|
||||
void Location::EncodeLocationFor(internal::AssemblerBase* assembler,
|
||||
int32_t from,
|
||||
const Location::EmitOperator* encoder) {
|
||||
if (encoder->IsUsingT32()) {
|
||||
uint16_t* instr_ptr =
|
||||
assembler->GetBuffer()->GetOffsetAddress<uint16_t*>(from);
|
||||
if (Is16BitEncoding(instr_ptr[0])) {
|
||||
// The Encode methods always deals with uint32_t types so we need
|
||||
// to explicitly cast it.
|
||||
uint32_t instr = static_cast<uint32_t>(instr_ptr[0]);
|
||||
instr = encoder->Encode(instr, from, this);
|
||||
// The Encode method should not ever set the top 16 bits.
|
||||
VIXL_ASSERT((instr & ~0xffff) == 0);
|
||||
instr_ptr[0] = static_cast<uint16_t>(instr);
|
||||
} else {
|
||||
uint32_t instr =
|
||||
instr_ptr[1] | (static_cast<uint32_t>(instr_ptr[0]) << 16);
|
||||
instr = encoder->Encode(instr, from, this);
|
||||
instr_ptr[0] = static_cast<uint16_t>(instr >> 16);
|
||||
instr_ptr[1] = static_cast<uint16_t>(instr);
|
||||
}
|
||||
} else {
|
||||
uint32_t* instr_ptr =
|
||||
assembler->GetBuffer()->GetOffsetAddress<uint32_t*>(from);
|
||||
instr_ptr[0] = encoder->Encode(instr_ptr[0], from, this);
|
||||
}
|
||||
}
|
||||
|
||||
void Location::AddForwardRef(int32_t instr_location,
|
||||
const EmitOperator& op,
|
||||
const ReferenceInfo* info) {
|
||||
VIXL_ASSERT(referenced_);
|
||||
int32_t from = instr_location + (op.IsUsingT32() ? kT32PcDelta : kA32PcDelta);
|
||||
if (info->pc_needs_aligning == ReferenceInfo::kAlignPc)
|
||||
from = AlignDown(from, 4);
|
||||
int32_t min_object_location = from + info->min_offset;
|
||||
int32_t max_object_location = from + info->max_offset;
|
||||
forward_.insert(ForwardRef(&op,
|
||||
instr_location,
|
||||
info->size,
|
||||
min_object_location,
|
||||
max_object_location,
|
||||
info->alignment));
|
||||
}
|
||||
|
||||
int Location::GetMaxAlignment() const {
|
||||
int max_alignment = GetPoolObjectAlignment();
|
||||
for (ForwardRefListIterator it(const_cast<Location*>(this)); !it.Done();
|
||||
it.Advance()) {
|
||||
const ForwardRef& reference = *it.Current();
|
||||
if (reference.GetAlignment() > max_alignment)
|
||||
max_alignment = reference.GetAlignment();
|
||||
}
|
||||
return max_alignment;
|
||||
}
|
||||
|
||||
int Location::GetMinLocation() const {
|
||||
int32_t min_location = 0;
|
||||
for (ForwardRefListIterator it(const_cast<Location*>(this)); !it.Done();
|
||||
it.Advance()) {
|
||||
const ForwardRef& reference = *it.Current();
|
||||
if (reference.GetMinLocation() > min_location)
|
||||
min_location = reference.GetMinLocation();
|
||||
}
|
||||
return min_location;
|
||||
}
|
||||
|
||||
void Label::UpdatePoolObject(PoolObject<int32_t>* object) {
|
||||
VIXL_ASSERT(forward_.size() == 1);
|
||||
const ForwardRef& reference = forward_.Front();
|
||||
object->Update(reference.GetMinLocation(),
|
||||
reference.GetMaxLocation(),
|
||||
reference.GetAlignment());
|
||||
}
|
||||
|
||||
void Label::EmitPoolObject(MacroAssemblerInterface* masm) {
|
||||
MacroAssembler* macro_assembler = static_cast<MacroAssembler*>(masm);
|
||||
|
||||
// Add a new branch to this label.
|
||||
macro_assembler->GetBuffer()->EnsureSpaceFor(kMaxInstructionSizeInBytes);
|
||||
ExactAssemblyScopeWithoutPoolsCheck guard(macro_assembler,
|
||||
kMaxInstructionSizeInBytes,
|
||||
ExactAssemblyScope::kMaximumSize);
|
||||
macro_assembler->b(this);
|
||||
}
|
||||
|
||||
void RawLiteral::EmitPoolObject(MacroAssemblerInterface* masm) {
|
||||
Assembler* assembler = static_cast<Assembler*>(masm->AsAssemblerBase());
|
||||
|
||||
assembler->GetBuffer()->EnsureSpaceFor(GetSize());
|
||||
assembler->GetBuffer()->EmitData(GetDataAddress(), GetSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,563 @@
|
||||
// Copyright 2017, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
extern "C" {
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
}
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#include "utils-vixl.h"
|
||||
#include "aarch32/constants-aarch32.h"
|
||||
#include "aarch32/instructions-aarch32.h"
|
||||
#include "aarch32/operands-aarch32.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch32 {
|
||||
|
||||
// Operand
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Operand& operand) {
|
||||
if (operand.IsImmediate()) {
|
||||
return os << "#" << operand.GetImmediate();
|
||||
}
|
||||
if (operand.IsImmediateShiftedRegister()) {
|
||||
if ((operand.GetShift().IsLSL() || operand.GetShift().IsROR()) &&
|
||||
(operand.GetShiftAmount() == 0)) {
|
||||
return os << operand.GetBaseRegister();
|
||||
}
|
||||
if (operand.GetShift().IsRRX()) {
|
||||
return os << operand.GetBaseRegister() << ", rrx";
|
||||
}
|
||||
return os << operand.GetBaseRegister() << ", " << operand.GetShift() << " #"
|
||||
<< operand.GetShiftAmount();
|
||||
}
|
||||
if (operand.IsRegisterShiftedRegister()) {
|
||||
return os << operand.GetBaseRegister() << ", " << operand.GetShift() << " "
|
||||
<< operand.GetShiftRegister();
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const NeonImmediate& neon_imm) {
|
||||
if (neon_imm.IsDouble()) {
|
||||
if (neon_imm.imm_.d_ == 0) {
|
||||
if (copysign(1.0, neon_imm.imm_.d_) < 0.0) {
|
||||
return os << "#-0.0";
|
||||
}
|
||||
return os << "#0.0";
|
||||
}
|
||||
return os << "#" << std::setprecision(9) << neon_imm.imm_.d_;
|
||||
}
|
||||
if (neon_imm.IsFloat()) {
|
||||
if (neon_imm.imm_.f_ == 0) {
|
||||
if (copysign(1.0, neon_imm.imm_.d_) < 0.0) return os << "#-0.0";
|
||||
return os << "#0.0";
|
||||
}
|
||||
return os << "#" << std::setprecision(9) << neon_imm.imm_.f_;
|
||||
}
|
||||
if (neon_imm.IsInteger64()) {
|
||||
return os << "#0x" << std::hex << std::setw(16) << std::setfill('0')
|
||||
<< neon_imm.imm_.u64_ << std::dec;
|
||||
}
|
||||
return os << "#" << neon_imm.imm_.u32_;
|
||||
}
|
||||
|
||||
// SOperand
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const SOperand& operand) {
|
||||
if (operand.IsImmediate()) {
|
||||
return os << operand.GetNeonImmediate();
|
||||
}
|
||||
return os << operand.GetRegister();
|
||||
}
|
||||
|
||||
// DOperand
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DOperand& operand) {
|
||||
if (operand.IsImmediate()) {
|
||||
return os << operand.GetNeonImmediate();
|
||||
}
|
||||
return os << operand.GetRegister();
|
||||
}
|
||||
|
||||
// QOperand
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const QOperand& operand) {
|
||||
if (operand.IsImmediate()) {
|
||||
return os << operand.GetNeonImmediate();
|
||||
}
|
||||
return os << operand.GetRegister();
|
||||
}
|
||||
|
||||
|
||||
ImmediateVbic::ImmediateVbic(DataType dt, const NeonImmediate& neon_imm) {
|
||||
if (neon_imm.IsInteger32()) {
|
||||
uint32_t immediate = neon_imm.GetImmediate<uint32_t>();
|
||||
if (dt.GetValue() == I16) {
|
||||
if ((immediate & ~0xff) == 0) {
|
||||
SetEncodingValue(0x9);
|
||||
SetEncodedImmediate(immediate);
|
||||
} else if ((immediate & ~0xff00) == 0) {
|
||||
SetEncodingValue(0xb);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
}
|
||||
} else if (dt.GetValue() == I32) {
|
||||
if ((immediate & ~0xff) == 0) {
|
||||
SetEncodingValue(0x1);
|
||||
SetEncodedImmediate(immediate);
|
||||
} else if ((immediate & ~0xff00) == 0) {
|
||||
SetEncodingValue(0x3);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
} else if ((immediate & ~0xff0000) == 0) {
|
||||
SetEncodingValue(0x5);
|
||||
SetEncodedImmediate(immediate >> 16);
|
||||
} else if ((immediate & ~0xff000000) == 0) {
|
||||
SetEncodingValue(0x7);
|
||||
SetEncodedImmediate(immediate >> 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DataType ImmediateVbic::DecodeDt(uint32_t cmode) {
|
||||
switch (cmode) {
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
case 0x5:
|
||||
case 0x7:
|
||||
return I32;
|
||||
case 0x9:
|
||||
case 0xb:
|
||||
return I16;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return kDataTypeValueInvalid;
|
||||
}
|
||||
|
||||
|
||||
NeonImmediate ImmediateVbic::DecodeImmediate(uint32_t cmode,
|
||||
uint32_t immediate) {
|
||||
switch (cmode) {
|
||||
case 0x1:
|
||||
case 0x9:
|
||||
return immediate;
|
||||
case 0x3:
|
||||
case 0xb:
|
||||
return immediate << 8;
|
||||
case 0x5:
|
||||
return immediate << 16;
|
||||
case 0x7:
|
||||
return immediate << 24;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ImmediateVmov::ImmediateVmov(DataType dt, const NeonImmediate& neon_imm) {
|
||||
if (neon_imm.IsInteger()) {
|
||||
switch (dt.GetValue()) {
|
||||
case I8:
|
||||
if (neon_imm.CanConvert<uint8_t>()) {
|
||||
SetEncodingValue(0xe);
|
||||
SetEncodedImmediate(neon_imm.GetImmediate<uint8_t>());
|
||||
}
|
||||
break;
|
||||
case I16:
|
||||
if (neon_imm.IsInteger32()) {
|
||||
uint32_t immediate = neon_imm.GetImmediate<uint32_t>();
|
||||
if ((immediate & ~0xff) == 0) {
|
||||
SetEncodingValue(0x8);
|
||||
SetEncodedImmediate(immediate);
|
||||
} else if ((immediate & ~0xff00) == 0) {
|
||||
SetEncodingValue(0xa);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case I32:
|
||||
if (neon_imm.IsInteger32()) {
|
||||
uint32_t immediate = neon_imm.GetImmediate<uint32_t>();
|
||||
if ((immediate & ~0xff) == 0) {
|
||||
SetEncodingValue(0x0);
|
||||
SetEncodedImmediate(immediate);
|
||||
} else if ((immediate & ~0xff00) == 0) {
|
||||
SetEncodingValue(0x2);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
} else if ((immediate & ~0xff0000) == 0) {
|
||||
SetEncodingValue(0x4);
|
||||
SetEncodedImmediate(immediate >> 16);
|
||||
} else if ((immediate & ~0xff000000) == 0) {
|
||||
SetEncodingValue(0x6);
|
||||
SetEncodedImmediate(immediate >> 24);
|
||||
} else if ((immediate & ~0xff00) == 0xff) {
|
||||
SetEncodingValue(0xc);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
} else if ((immediate & ~0xff0000) == 0xffff) {
|
||||
SetEncodingValue(0xd);
|
||||
SetEncodedImmediate(immediate >> 16);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case I64: {
|
||||
bool is_valid = true;
|
||||
uint32_t encoding = 0;
|
||||
if (neon_imm.IsInteger32()) {
|
||||
uint32_t immediate = neon_imm.GetImmediate<uint32_t>();
|
||||
uint32_t mask = 0xff000000;
|
||||
for (uint32_t set_bit = 1 << 3; set_bit != 0; set_bit >>= 1) {
|
||||
if ((immediate & mask) == mask) {
|
||||
encoding |= set_bit;
|
||||
} else if ((immediate & mask) != 0) {
|
||||
is_valid = false;
|
||||
break;
|
||||
}
|
||||
mask >>= 8;
|
||||
}
|
||||
} else {
|
||||
uint64_t immediate = neon_imm.GetImmediate<uint64_t>();
|
||||
uint64_t mask = UINT64_C(0xff) << 56;
|
||||
for (uint32_t set_bit = 1 << 7; set_bit != 0; set_bit >>= 1) {
|
||||
if ((immediate & mask) == mask) {
|
||||
encoding |= set_bit;
|
||||
} else if ((immediate & mask) != 0) {
|
||||
is_valid = false;
|
||||
break;
|
||||
}
|
||||
mask >>= 8;
|
||||
}
|
||||
}
|
||||
if (is_valid) {
|
||||
SetEncodingValue(0x1e);
|
||||
SetEncodedImmediate(encoding);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (dt.GetValue()) {
|
||||
case F32:
|
||||
if (neon_imm.IsFloat() || neon_imm.IsDouble()) {
|
||||
ImmediateVFP vfp(neon_imm.GetImmediate<float>());
|
||||
if (vfp.IsValid()) {
|
||||
SetEncodingValue(0xf);
|
||||
SetEncodedImmediate(vfp.GetEncodingValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DataType ImmediateVmov::DecodeDt(uint32_t cmode) {
|
||||
switch (cmode & 0xf) {
|
||||
case 0x0:
|
||||
case 0x2:
|
||||
case 0x4:
|
||||
case 0x6:
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
return I32;
|
||||
case 0x8:
|
||||
case 0xa:
|
||||
return I16;
|
||||
case 0xe:
|
||||
return ((cmode & 0x10) == 0) ? I8 : I64;
|
||||
case 0xf:
|
||||
if ((cmode & 0x10) == 0) return F32;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return kDataTypeValueInvalid;
|
||||
}
|
||||
|
||||
|
||||
NeonImmediate ImmediateVmov::DecodeImmediate(uint32_t cmode,
|
||||
uint32_t immediate) {
|
||||
switch (cmode & 0xf) {
|
||||
case 0x8:
|
||||
case 0x0:
|
||||
return immediate;
|
||||
case 0x2:
|
||||
case 0xa:
|
||||
return immediate << 8;
|
||||
case 0x4:
|
||||
return immediate << 16;
|
||||
case 0x6:
|
||||
return immediate << 24;
|
||||
case 0xc:
|
||||
return (immediate << 8) | 0xff;
|
||||
case 0xd:
|
||||
return (immediate << 16) | 0xffff;
|
||||
case 0xe: {
|
||||
if (cmode == 0x1e) {
|
||||
uint64_t encoding = 0;
|
||||
for (uint32_t set_bit = 1 << 7; set_bit != 0; set_bit >>= 1) {
|
||||
encoding <<= 8;
|
||||
if ((immediate & set_bit) != 0) {
|
||||
encoding |= 0xff;
|
||||
}
|
||||
}
|
||||
return encoding;
|
||||
} else {
|
||||
return immediate;
|
||||
}
|
||||
}
|
||||
case 0xf: {
|
||||
return ImmediateVFP::Decode<float>(immediate);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ImmediateVmvn::ImmediateVmvn(DataType dt, const NeonImmediate& neon_imm) {
|
||||
if (neon_imm.IsInteger32()) {
|
||||
uint32_t immediate = neon_imm.GetImmediate<uint32_t>();
|
||||
switch (dt.GetValue()) {
|
||||
case I16:
|
||||
if ((immediate & ~0xff) == 0) {
|
||||
SetEncodingValue(0x8);
|
||||
SetEncodedImmediate(immediate);
|
||||
} else if ((immediate & ~0xff00) == 0) {
|
||||
SetEncodingValue(0xa);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
}
|
||||
break;
|
||||
case I32:
|
||||
if ((immediate & ~0xff) == 0) {
|
||||
SetEncodingValue(0x0);
|
||||
SetEncodedImmediate(immediate);
|
||||
} else if ((immediate & ~0xff00) == 0) {
|
||||
SetEncodingValue(0x2);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
} else if ((immediate & ~0xff0000) == 0) {
|
||||
SetEncodingValue(0x4);
|
||||
SetEncodedImmediate(immediate >> 16);
|
||||
} else if ((immediate & ~0xff000000) == 0) {
|
||||
SetEncodingValue(0x6);
|
||||
SetEncodedImmediate(immediate >> 24);
|
||||
} else if ((immediate & ~0xff00) == 0xff) {
|
||||
SetEncodingValue(0xc);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
} else if ((immediate & ~0xff0000) == 0xffff) {
|
||||
SetEncodingValue(0xd);
|
||||
SetEncodedImmediate(immediate >> 16);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DataType ImmediateVmvn::DecodeDt(uint32_t cmode) {
|
||||
switch (cmode) {
|
||||
case 0x0:
|
||||
case 0x2:
|
||||
case 0x4:
|
||||
case 0x6:
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
return I32;
|
||||
case 0x8:
|
||||
case 0xa:
|
||||
return I16;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return kDataTypeValueInvalid;
|
||||
}
|
||||
|
||||
|
||||
NeonImmediate ImmediateVmvn::DecodeImmediate(uint32_t cmode,
|
||||
uint32_t immediate) {
|
||||
switch (cmode) {
|
||||
case 0x0:
|
||||
case 0x8:
|
||||
return immediate;
|
||||
case 0x2:
|
||||
case 0xa:
|
||||
return immediate << 8;
|
||||
case 0x4:
|
||||
return immediate << 16;
|
||||
case 0x6:
|
||||
return immediate << 24;
|
||||
case 0xc:
|
||||
return (immediate << 8) | 0xff;
|
||||
case 0xd:
|
||||
return (immediate << 16) | 0xffff;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ImmediateVorr::ImmediateVorr(DataType dt, const NeonImmediate& neon_imm) {
|
||||
if (neon_imm.IsInteger32()) {
|
||||
uint32_t immediate = neon_imm.GetImmediate<uint32_t>();
|
||||
if (dt.GetValue() == I16) {
|
||||
if ((immediate & ~0xff) == 0) {
|
||||
SetEncodingValue(0x9);
|
||||
SetEncodedImmediate(immediate);
|
||||
} else if ((immediate & ~0xff00) == 0) {
|
||||
SetEncodingValue(0xb);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
}
|
||||
} else if (dt.GetValue() == I32) {
|
||||
if ((immediate & ~0xff) == 0) {
|
||||
SetEncodingValue(0x1);
|
||||
SetEncodedImmediate(immediate);
|
||||
} else if ((immediate & ~0xff00) == 0) {
|
||||
SetEncodingValue(0x3);
|
||||
SetEncodedImmediate(immediate >> 8);
|
||||
} else if ((immediate & ~0xff0000) == 0) {
|
||||
SetEncodingValue(0x5);
|
||||
SetEncodedImmediate(immediate >> 16);
|
||||
} else if ((immediate & ~0xff000000) == 0) {
|
||||
SetEncodingValue(0x7);
|
||||
SetEncodedImmediate(immediate >> 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DataType ImmediateVorr::DecodeDt(uint32_t cmode) {
|
||||
switch (cmode) {
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
case 0x5:
|
||||
case 0x7:
|
||||
return I32;
|
||||
case 0x9:
|
||||
case 0xb:
|
||||
return I16;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return kDataTypeValueInvalid;
|
||||
}
|
||||
|
||||
|
||||
NeonImmediate ImmediateVorr::DecodeImmediate(uint32_t cmode,
|
||||
uint32_t immediate) {
|
||||
switch (cmode) {
|
||||
case 0x1:
|
||||
case 0x9:
|
||||
return immediate;
|
||||
case 0x3:
|
||||
case 0xb:
|
||||
return immediate << 8;
|
||||
case 0x5:
|
||||
return immediate << 16;
|
||||
case 0x7:
|
||||
return immediate << 24;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// MemOperand
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const MemOperand& operand) {
|
||||
os << "[" << operand.GetBaseRegister();
|
||||
if (operand.GetAddrMode() == PostIndex) {
|
||||
os << "]";
|
||||
if (operand.IsRegisterOnly()) return os << "!";
|
||||
}
|
||||
if (operand.IsImmediate()) {
|
||||
if ((operand.GetOffsetImmediate() != 0) || operand.GetSign().IsMinus() ||
|
||||
((operand.GetAddrMode() != Offset) && !operand.IsRegisterOnly())) {
|
||||
if (operand.GetOffsetImmediate() == 0) {
|
||||
os << ", #" << operand.GetSign() << operand.GetOffsetImmediate();
|
||||
} else {
|
||||
os << ", #" << operand.GetOffsetImmediate();
|
||||
}
|
||||
}
|
||||
} else if (operand.IsPlainRegister()) {
|
||||
os << ", " << operand.GetSign() << operand.GetOffsetRegister();
|
||||
} else if (operand.IsShiftedRegister()) {
|
||||
os << ", " << operand.GetSign() << operand.GetOffsetRegister()
|
||||
<< ImmediateShiftOperand(operand.GetShift(), operand.GetShiftAmount());
|
||||
} else {
|
||||
VIXL_UNREACHABLE();
|
||||
return os;
|
||||
}
|
||||
if (operand.GetAddrMode() == Offset) {
|
||||
os << "]";
|
||||
} else if (operand.GetAddrMode() == PreIndex) {
|
||||
os << "]!";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const AlignedMemOperand& operand) {
|
||||
os << "[" << operand.GetBaseRegister() << operand.GetAlignment() << "]";
|
||||
if (operand.GetAddrMode() == PostIndex) {
|
||||
if (operand.IsPlainRegister()) {
|
||||
os << ", " << operand.GetOffsetRegister();
|
||||
} else {
|
||||
os << "!";
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace aarch32
|
||||
} // namespace vixl
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,178 @@
|
||||
// Copyright 2015, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "../utils-vixl.h"
|
||||
|
||||
#include "cpu-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
// Initialise to smallest possible cache size.
|
||||
unsigned CPU::dcache_line_size_ = 1;
|
||||
unsigned CPU::icache_line_size_ = 1;
|
||||
|
||||
|
||||
// Currently computes I and D cache line size.
|
||||
void CPU::SetUp() {
|
||||
uint32_t cache_type_register = GetCacheType();
|
||||
|
||||
// The cache type register holds information about the caches, including I
|
||||
// D caches line size.
|
||||
static const int kDCacheLineSizeShift = 16;
|
||||
static const int kICacheLineSizeShift = 0;
|
||||
static const uint32_t kDCacheLineSizeMask = 0xf << kDCacheLineSizeShift;
|
||||
static const uint32_t kICacheLineSizeMask = 0xf << kICacheLineSizeShift;
|
||||
|
||||
// The cache type register holds the size of the I and D caches in words as
|
||||
// a power of two.
|
||||
uint32_t dcache_line_size_power_of_two =
|
||||
(cache_type_register & kDCacheLineSizeMask) >> kDCacheLineSizeShift;
|
||||
uint32_t icache_line_size_power_of_two =
|
||||
(cache_type_register & kICacheLineSizeMask) >> kICacheLineSizeShift;
|
||||
|
||||
dcache_line_size_ = 4 << dcache_line_size_power_of_two;
|
||||
icache_line_size_ = 4 << icache_line_size_power_of_two;
|
||||
}
|
||||
|
||||
|
||||
uint32_t CPU::GetCacheType() {
|
||||
#ifdef __aarch64__
|
||||
uint64_t cache_type_register;
|
||||
// Copy the content of the cache type register to a core register.
|
||||
__asm__ __volatile__("mrs %[ctr], ctr_el0" // NOLINT(runtime/references)
|
||||
: [ctr] "=r"(cache_type_register));
|
||||
VIXL_ASSERT(IsUint32(cache_type_register));
|
||||
return static_cast<uint32_t>(cache_type_register);
|
||||
#else
|
||||
// This will lead to a cache with 1 byte long lines, which is fine since
|
||||
// neither EnsureIAndDCacheCoherency nor the simulator will need this
|
||||
// information.
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CPU::EnsureIAndDCacheCoherency(void *address, size_t length) {
|
||||
#ifdef __aarch64__
|
||||
// Implement the cache synchronisation for all targets where AArch64 is the
|
||||
// host, even if we're building the simulator for an AAarch64 host. This
|
||||
// allows for cases where the user wants to simulate code as well as run it
|
||||
// natively.
|
||||
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The code below assumes user space cache operations are allowed.
|
||||
|
||||
// Work out the line sizes for each cache, and use them to determine the
|
||||
// start addresses.
|
||||
uintptr_t start = reinterpret_cast<uintptr_t>(address);
|
||||
uintptr_t dsize = static_cast<uintptr_t>(dcache_line_size_);
|
||||
uintptr_t isize = static_cast<uintptr_t>(icache_line_size_);
|
||||
uintptr_t dline = start & ~(dsize - 1);
|
||||
uintptr_t iline = start & ~(isize - 1);
|
||||
|
||||
// Cache line sizes are always a power of 2.
|
||||
VIXL_ASSERT(IsPowerOf2(dsize));
|
||||
VIXL_ASSERT(IsPowerOf2(isize));
|
||||
uintptr_t end = start + length;
|
||||
|
||||
do {
|
||||
__asm__ __volatile__(
|
||||
// Clean each line of the D cache containing the target data.
|
||||
//
|
||||
// dc : Data Cache maintenance
|
||||
// c : Clean
|
||||
// va : by (Virtual) Address
|
||||
// u : to the point of Unification
|
||||
// The point of unification for a processor is the point by which the
|
||||
// instruction and data caches are guaranteed to see the same copy of a
|
||||
// memory location. See ARM DDI 0406B page B2-12 for more information.
|
||||
" dc cvau, %[dline]\n"
|
||||
:
|
||||
: [dline] "r"(dline)
|
||||
// This code does not write to memory, but the "memory" dependency
|
||||
// prevents GCC from reordering the code.
|
||||
: "memory");
|
||||
dline += dsize;
|
||||
} while (dline < end);
|
||||
|
||||
__asm__ __volatile__(
|
||||
// Make sure that the data cache operations (above) complete before the
|
||||
// instruction cache operations (below).
|
||||
//
|
||||
// dsb : Data Synchronisation Barrier
|
||||
// ish : Inner SHareable domain
|
||||
//
|
||||
// The point of unification for an Inner Shareable shareability domain is
|
||||
// the point by which the instruction and data caches of all the
|
||||
// processors
|
||||
// in that Inner Shareable shareability domain are guaranteed to see the
|
||||
// same copy of a memory location. See ARM DDI 0406B page B2-12 for more
|
||||
// information.
|
||||
" dsb ish\n"
|
||||
:
|
||||
:
|
||||
: "memory");
|
||||
|
||||
do {
|
||||
__asm__ __volatile__(
|
||||
// Invalidate each line of the I cache containing the target data.
|
||||
//
|
||||
// ic : Instruction Cache maintenance
|
||||
// i : Invalidate
|
||||
// va : by Address
|
||||
// u : to the point of Unification
|
||||
" ic ivau, %[iline]\n"
|
||||
:
|
||||
: [iline] "r"(iline)
|
||||
: "memory");
|
||||
iline += isize;
|
||||
} while (iline < end);
|
||||
|
||||
__asm__ __volatile__(
|
||||
// Make sure that the instruction cache operations (above) take effect
|
||||
// before the isb (below).
|
||||
" dsb ish\n"
|
||||
|
||||
// Ensure that any instructions already in the pipeline are discarded and
|
||||
// reloaded from the new data.
|
||||
// isb : Instruction Synchronisation Barrier
|
||||
" isb\n"
|
||||
:
|
||||
:
|
||||
: "memory");
|
||||
#else
|
||||
// If the host isn't AArch64, we must be using the simulator, so this function
|
||||
// doesn't have to do anything.
|
||||
USE(address, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,713 @@
|
||||
// Copyright 2015, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "instructions-aarch64.h"
|
||||
#include "assembler-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
static uint64_t RepeatBitsAcrossReg(unsigned reg_size,
|
||||
uint64_t value,
|
||||
unsigned width) {
|
||||
VIXL_ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) ||
|
||||
(width == 32));
|
||||
VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize));
|
||||
uint64_t result = value & ((UINT64_C(1) << width) - 1);
|
||||
for (unsigned i = width; i < reg_size; i *= 2) {
|
||||
result |= (result << i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool Instruction::IsLoad() const {
|
||||
if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) {
|
||||
return Mask(LoadStorePairLBit) != 0;
|
||||
} else {
|
||||
LoadStoreOp op = static_cast<LoadStoreOp>(Mask(LoadStoreMask));
|
||||
switch (op) {
|
||||
case LDRB_w:
|
||||
case LDRH_w:
|
||||
case LDR_w:
|
||||
case LDR_x:
|
||||
case LDRSB_w:
|
||||
case LDRSB_x:
|
||||
case LDRSH_w:
|
||||
case LDRSH_x:
|
||||
case LDRSW_x:
|
||||
case LDR_b:
|
||||
case LDR_h:
|
||||
case LDR_s:
|
||||
case LDR_d:
|
||||
case LDR_q:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Instruction::IsStore() const {
|
||||
if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) {
|
||||
return Mask(LoadStorePairLBit) == 0;
|
||||
} else {
|
||||
LoadStoreOp op = static_cast<LoadStoreOp>(Mask(LoadStoreMask));
|
||||
switch (op) {
|
||||
case STRB_w:
|
||||
case STRH_w:
|
||||
case STR_w:
|
||||
case STR_x:
|
||||
case STR_b:
|
||||
case STR_h:
|
||||
case STR_s:
|
||||
case STR_d:
|
||||
case STR_q:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Logical immediates can't encode zero, so a return value of zero is used to
|
||||
// indicate a failure case. Specifically, where the constraints on imm_s are
|
||||
// not met.
|
||||
uint64_t Instruction::GetImmLogical() const {
|
||||
unsigned reg_size = GetSixtyFourBits() ? kXRegSize : kWRegSize;
|
||||
int32_t n = GetBitN();
|
||||
int32_t imm_s = GetImmSetBits();
|
||||
int32_t imm_r = GetImmRotate();
|
||||
|
||||
// An integer is constructed from the n, imm_s and imm_r bits according to
|
||||
// the following table:
|
||||
//
|
||||
// N imms immr size S R
|
||||
// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
|
||||
// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
|
||||
// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
|
||||
// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
|
||||
// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
|
||||
// 0 11110s xxxxxr 2 UInt(s) UInt(r)
|
||||
// (s bits must not be all set)
|
||||
//
|
||||
// A pattern is constructed of size bits, where the least significant S+1
|
||||
// bits are set. The pattern is rotated right by R, and repeated across a
|
||||
// 32 or 64-bit value, depending on destination register width.
|
||||
//
|
||||
|
||||
if (n == 1) {
|
||||
if (imm_s == 0x3f) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t bits = (UINT64_C(1) << (imm_s + 1)) - 1;
|
||||
return RotateRight(bits, imm_r, 64);
|
||||
} else {
|
||||
if ((imm_s >> 1) == 0x1f) {
|
||||
return 0;
|
||||
}
|
||||
for (int width = 0x20; width >= 0x2; width >>= 1) {
|
||||
if ((imm_s & width) == 0) {
|
||||
int mask = width - 1;
|
||||
if ((imm_s & mask) == mask) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t bits = (UINT64_C(1) << ((imm_s & mask) + 1)) - 1;
|
||||
return RepeatBitsAcrossReg(reg_size,
|
||||
RotateRight(bits, imm_r & mask, width),
|
||||
width);
|
||||
}
|
||||
}
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Instruction::GetImmNEONabcdefgh() const {
|
||||
return GetImmNEONabc() << 5 | GetImmNEONdefgh();
|
||||
}
|
||||
|
||||
|
||||
Float16 Instruction::Imm8ToFloat16(uint32_t imm8) {
|
||||
// Imm8: abcdefgh (8 bits)
|
||||
// Half: aBbb.cdef.gh00.0000 (16 bits)
|
||||
// where B is b ^ 1
|
||||
uint32_t bits = imm8;
|
||||
uint16_t bit7 = (bits >> 7) & 0x1;
|
||||
uint16_t bit6 = (bits >> 6) & 0x1;
|
||||
uint16_t bit5_to_0 = bits & 0x3f;
|
||||
uint16_t result = (bit7 << 15) | ((4 - bit6) << 12) | (bit5_to_0 << 6);
|
||||
return RawbitsToFloat16(result);
|
||||
}
|
||||
|
||||
|
||||
float Instruction::Imm8ToFP32(uint32_t imm8) {
|
||||
// Imm8: abcdefgh (8 bits)
|
||||
// Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits)
|
||||
// where B is b ^ 1
|
||||
uint32_t bits = imm8;
|
||||
uint32_t bit7 = (bits >> 7) & 0x1;
|
||||
uint32_t bit6 = (bits >> 6) & 0x1;
|
||||
uint32_t bit5_to_0 = bits & 0x3f;
|
||||
uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19);
|
||||
|
||||
return RawbitsToFloat(result);
|
||||
}
|
||||
|
||||
|
||||
Float16 Instruction::GetImmFP16() const { return Imm8ToFloat16(GetImmFP()); }
|
||||
|
||||
|
||||
float Instruction::GetImmFP32() const { return Imm8ToFP32(GetImmFP()); }
|
||||
|
||||
|
||||
double Instruction::Imm8ToFP64(uint32_t imm8) {
|
||||
// Imm8: abcdefgh (8 bits)
|
||||
// Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000
|
||||
// 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits)
|
||||
// where B is b ^ 1
|
||||
uint32_t bits = imm8;
|
||||
uint64_t bit7 = (bits >> 7) & 0x1;
|
||||
uint64_t bit6 = (bits >> 6) & 0x1;
|
||||
uint64_t bit5_to_0 = bits & 0x3f;
|
||||
uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48);
|
||||
|
||||
return RawbitsToDouble(result);
|
||||
}
|
||||
|
||||
|
||||
double Instruction::GetImmFP64() const { return Imm8ToFP64(GetImmFP()); }
|
||||
|
||||
|
||||
Float16 Instruction::GetImmNEONFP16() const {
|
||||
return Imm8ToFloat16(GetImmNEONabcdefgh());
|
||||
}
|
||||
|
||||
|
||||
float Instruction::GetImmNEONFP32() const {
|
||||
return Imm8ToFP32(GetImmNEONabcdefgh());
|
||||
}
|
||||
|
||||
|
||||
double Instruction::GetImmNEONFP64() const {
|
||||
return Imm8ToFP64(GetImmNEONabcdefgh());
|
||||
}
|
||||
|
||||
|
||||
unsigned CalcLSDataSize(LoadStoreOp op) {
|
||||
VIXL_ASSERT((LSSize_offset + LSSize_width) == (kInstructionSize * 8));
|
||||
unsigned size = static_cast<Instr>(op) >> LSSize_offset;
|
||||
if ((op & LSVector_mask) != 0) {
|
||||
// Vector register memory operations encode the access size in the "size"
|
||||
// and "opc" fields.
|
||||
if ((size == 0) && ((op & LSOpc_mask) >> LSOpc_offset) >= 2) {
|
||||
size = kQRegSizeInBytesLog2;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
unsigned CalcLSPairDataSize(LoadStorePairOp op) {
|
||||
VIXL_STATIC_ASSERT(kXRegSizeInBytes == kDRegSizeInBytes);
|
||||
VIXL_STATIC_ASSERT(kWRegSizeInBytes == kSRegSizeInBytes);
|
||||
switch (op) {
|
||||
case STP_q:
|
||||
case LDP_q:
|
||||
return kQRegSizeInBytesLog2;
|
||||
case STP_x:
|
||||
case LDP_x:
|
||||
case STP_d:
|
||||
case LDP_d:
|
||||
return kXRegSizeInBytesLog2;
|
||||
default:
|
||||
return kWRegSizeInBytesLog2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Instruction::GetImmBranchRangeBitwidth(ImmBranchType branch_type) {
|
||||
switch (branch_type) {
|
||||
case UncondBranchType:
|
||||
return ImmUncondBranch_width;
|
||||
case CondBranchType:
|
||||
return ImmCondBranch_width;
|
||||
case CompareBranchType:
|
||||
return ImmCmpBranch_width;
|
||||
case TestBranchType:
|
||||
return ImmTestBranch_width;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32_t Instruction::GetImmBranchForwardRange(ImmBranchType branch_type) {
|
||||
int32_t encoded_max = 1 << (GetImmBranchRangeBitwidth(branch_type) - 1);
|
||||
return encoded_max * kInstructionSize;
|
||||
}
|
||||
|
||||
|
||||
bool Instruction::IsValidImmPCOffset(ImmBranchType branch_type,
|
||||
int64_t offset) {
|
||||
return IsIntN(GetImmBranchRangeBitwidth(branch_type), offset);
|
||||
}
|
||||
|
||||
|
||||
const Instruction* Instruction::GetImmPCOffsetTarget() const {
|
||||
const Instruction* base = this;
|
||||
ptrdiff_t offset;
|
||||
if (IsPCRelAddressing()) {
|
||||
// ADR and ADRP.
|
||||
offset = GetImmPCRel();
|
||||
if (Mask(PCRelAddressingMask) == ADRP) {
|
||||
base = AlignDown(base, kPageSize);
|
||||
offset *= kPageSize;
|
||||
} else {
|
||||
VIXL_ASSERT(Mask(PCRelAddressingMask) == ADR);
|
||||
}
|
||||
} else {
|
||||
// All PC-relative branches.
|
||||
VIXL_ASSERT(GetBranchType() != UnknownBranchType);
|
||||
// Relative branch offsets are instruction-size-aligned.
|
||||
offset = GetImmBranch() * static_cast<int>(kInstructionSize);
|
||||
}
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
|
||||
int Instruction::GetImmBranch() const {
|
||||
switch (GetBranchType()) {
|
||||
case CondBranchType:
|
||||
return GetImmCondBranch();
|
||||
case UncondBranchType:
|
||||
return GetImmUncondBranch();
|
||||
case CompareBranchType:
|
||||
return GetImmCmpBranch();
|
||||
case TestBranchType:
|
||||
return GetImmTestBranch();
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetImmPCOffsetTarget(const Instruction* target) {
|
||||
if (IsPCRelAddressing()) {
|
||||
SetPCRelImmTarget(target);
|
||||
} else {
|
||||
SetBranchImmTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetPCRelImmTarget(const Instruction* target) {
|
||||
ptrdiff_t imm21;
|
||||
if ((Mask(PCRelAddressingMask) == ADR)) {
|
||||
imm21 = target - this;
|
||||
} else {
|
||||
VIXL_ASSERT(Mask(PCRelAddressingMask) == ADRP);
|
||||
uintptr_t this_page = reinterpret_cast<uintptr_t>(this) / kPageSize;
|
||||
uintptr_t target_page = reinterpret_cast<uintptr_t>(target) / kPageSize;
|
||||
imm21 = target_page - this_page;
|
||||
}
|
||||
Instr imm = Assembler::ImmPCRelAddress(static_cast<int32_t>(imm21));
|
||||
|
||||
SetInstructionBits(Mask(~ImmPCRel_mask) | imm);
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetBranchImmTarget(const Instruction* target) {
|
||||
VIXL_ASSERT(((target - this) & 3) == 0);
|
||||
Instr branch_imm = 0;
|
||||
uint32_t imm_mask = 0;
|
||||
int offset = static_cast<int>((target - this) >> kInstructionSizeLog2);
|
||||
switch (GetBranchType()) {
|
||||
case CondBranchType: {
|
||||
branch_imm = Assembler::ImmCondBranch(offset);
|
||||
imm_mask = ImmCondBranch_mask;
|
||||
break;
|
||||
}
|
||||
case UncondBranchType: {
|
||||
branch_imm = Assembler::ImmUncondBranch(offset);
|
||||
imm_mask = ImmUncondBranch_mask;
|
||||
break;
|
||||
}
|
||||
case CompareBranchType: {
|
||||
branch_imm = Assembler::ImmCmpBranch(offset);
|
||||
imm_mask = ImmCmpBranch_mask;
|
||||
break;
|
||||
}
|
||||
case TestBranchType: {
|
||||
branch_imm = Assembler::ImmTestBranch(offset);
|
||||
imm_mask = ImmTestBranch_mask;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
}
|
||||
SetInstructionBits(Mask(~imm_mask) | branch_imm);
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetImmLLiteral(const Instruction* source) {
|
||||
VIXL_ASSERT(IsWordAligned(source));
|
||||
ptrdiff_t offset = (source - this) >> kLiteralEntrySizeLog2;
|
||||
Instr imm = Assembler::ImmLLiteral(static_cast<int>(offset));
|
||||
Instr mask = ImmLLiteral_mask;
|
||||
|
||||
SetInstructionBits(Mask(~mask) | imm);
|
||||
}
|
||||
|
||||
|
||||
VectorFormat VectorFormatHalfWidth(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform == kFormat8H || vform == kFormat4S || vform == kFormat2D ||
|
||||
vform == kFormatH || vform == kFormatS || vform == kFormatD);
|
||||
switch (vform) {
|
||||
case kFormat8H:
|
||||
return kFormat8B;
|
||||
case kFormat4S:
|
||||
return kFormat4H;
|
||||
case kFormat2D:
|
||||
return kFormat2S;
|
||||
case kFormatH:
|
||||
return kFormatB;
|
||||
case kFormatS:
|
||||
return kFormatH;
|
||||
case kFormatD:
|
||||
return kFormatS;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorFormat VectorFormatDoubleWidth(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S ||
|
||||
vform == kFormatB || vform == kFormatH || vform == kFormatS);
|
||||
switch (vform) {
|
||||
case kFormat8B:
|
||||
return kFormat8H;
|
||||
case kFormat4H:
|
||||
return kFormat4S;
|
||||
case kFormat2S:
|
||||
return kFormat2D;
|
||||
case kFormatB:
|
||||
return kFormatH;
|
||||
case kFormatH:
|
||||
return kFormatS;
|
||||
case kFormatS:
|
||||
return kFormatD;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorFormat VectorFormatFillQ(VectorFormat vform) {
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormat8B:
|
||||
case kFormat16B:
|
||||
return kFormat16B;
|
||||
case kFormatH:
|
||||
case kFormat4H:
|
||||
case kFormat8H:
|
||||
return kFormat8H;
|
||||
case kFormatS:
|
||||
case kFormat2S:
|
||||
case kFormat4S:
|
||||
return kFormat4S;
|
||||
case kFormatD:
|
||||
case kFormat1D:
|
||||
case kFormat2D:
|
||||
return kFormat2D;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
VectorFormat VectorFormatHalfWidthDoubleLanes(VectorFormat vform) {
|
||||
switch (vform) {
|
||||
case kFormat4H:
|
||||
return kFormat8B;
|
||||
case kFormat8H:
|
||||
return kFormat16B;
|
||||
case kFormat2S:
|
||||
return kFormat4H;
|
||||
case kFormat4S:
|
||||
return kFormat8H;
|
||||
case kFormat1D:
|
||||
return kFormat2S;
|
||||
case kFormat2D:
|
||||
return kFormat4S;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
VectorFormat VectorFormatDoubleLanes(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S);
|
||||
switch (vform) {
|
||||
case kFormat8B:
|
||||
return kFormat16B;
|
||||
case kFormat4H:
|
||||
return kFormat8H;
|
||||
case kFormat2S:
|
||||
return kFormat4S;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorFormat VectorFormatHalfLanes(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform == kFormat16B || vform == kFormat8H || vform == kFormat4S);
|
||||
switch (vform) {
|
||||
case kFormat16B:
|
||||
return kFormat8B;
|
||||
case kFormat8H:
|
||||
return kFormat4H;
|
||||
case kFormat4S:
|
||||
return kFormat2S;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorFormat ScalarFormatFromLaneSize(int laneSize) {
|
||||
switch (laneSize) {
|
||||
case 8:
|
||||
return kFormatB;
|
||||
case 16:
|
||||
return kFormatH;
|
||||
case 32:
|
||||
return kFormatS;
|
||||
case 64:
|
||||
return kFormatD;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorFormat ScalarFormatFromFormat(VectorFormat vform) {
|
||||
return ScalarFormatFromLaneSize(LaneSizeInBitsFromFormat(vform));
|
||||
}
|
||||
|
||||
|
||||
unsigned RegisterSizeInBitsFromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
return kBRegSize;
|
||||
case kFormatH:
|
||||
return kHRegSize;
|
||||
case kFormatS:
|
||||
case kFormat2H:
|
||||
return kSRegSize;
|
||||
case kFormatD:
|
||||
return kDRegSize;
|
||||
case kFormat8B:
|
||||
case kFormat4H:
|
||||
case kFormat2S:
|
||||
case kFormat1D:
|
||||
return kDRegSize;
|
||||
default:
|
||||
return kQRegSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned RegisterSizeInBytesFromFormat(VectorFormat vform) {
|
||||
return RegisterSizeInBitsFromFormat(vform) / 8;
|
||||
}
|
||||
|
||||
|
||||
unsigned LaneSizeInBitsFromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormat8B:
|
||||
case kFormat16B:
|
||||
return 8;
|
||||
case kFormatH:
|
||||
case kFormat2H:
|
||||
case kFormat4H:
|
||||
case kFormat8H:
|
||||
return 16;
|
||||
case kFormatS:
|
||||
case kFormat2S:
|
||||
case kFormat4S:
|
||||
return 32;
|
||||
case kFormatD:
|
||||
case kFormat1D:
|
||||
case kFormat2D:
|
||||
return 64;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int LaneSizeInBytesFromFormat(VectorFormat vform) {
|
||||
return LaneSizeInBitsFromFormat(vform) / 8;
|
||||
}
|
||||
|
||||
|
||||
int LaneSizeInBytesLog2FromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormat8B:
|
||||
case kFormat16B:
|
||||
return 0;
|
||||
case kFormatH:
|
||||
case kFormat2H:
|
||||
case kFormat4H:
|
||||
case kFormat8H:
|
||||
return 1;
|
||||
case kFormatS:
|
||||
case kFormat2S:
|
||||
case kFormat4S:
|
||||
return 2;
|
||||
case kFormatD:
|
||||
case kFormat1D:
|
||||
case kFormat2D:
|
||||
return 3;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int LaneCountFromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormat16B:
|
||||
return 16;
|
||||
case kFormat8B:
|
||||
case kFormat8H:
|
||||
return 8;
|
||||
case kFormat4H:
|
||||
case kFormat4S:
|
||||
return 4;
|
||||
case kFormat2H:
|
||||
case kFormat2S:
|
||||
case kFormat2D:
|
||||
return 2;
|
||||
case kFormat1D:
|
||||
case kFormatB:
|
||||
case kFormatH:
|
||||
case kFormatS:
|
||||
case kFormatD:
|
||||
return 1;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int MaxLaneCountFromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormat8B:
|
||||
case kFormat16B:
|
||||
return 16;
|
||||
case kFormatH:
|
||||
case kFormat4H:
|
||||
case kFormat8H:
|
||||
return 8;
|
||||
case kFormatS:
|
||||
case kFormat2S:
|
||||
case kFormat4S:
|
||||
return 4;
|
||||
case kFormatD:
|
||||
case kFormat1D:
|
||||
case kFormat2D:
|
||||
return 2;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Does 'vform' indicate a vector format or a scalar format?
|
||||
bool IsVectorFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormatH:
|
||||
case kFormatS:
|
||||
case kFormatD:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int64_t MaxIntFromFormat(VectorFormat vform) {
|
||||
return INT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform));
|
||||
}
|
||||
|
||||
|
||||
int64_t MinIntFromFormat(VectorFormat vform) {
|
||||
return INT64_MIN >> (64 - LaneSizeInBitsFromFormat(vform));
|
||||
}
|
||||
|
||||
|
||||
uint64_t MaxUintFromFormat(VectorFormat vform) {
|
||||
return UINT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform));
|
||||
}
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
@@ -0,0 +1,916 @@
|
||||
// Copyright 2014, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "instrument-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
Counter::Counter(const char* name, CounterType type)
|
||||
: count_(0), enabled_(false), type_(type) {
|
||||
VIXL_ASSERT(name != NULL);
|
||||
strncpy(name_, name, kCounterNameMaxLength);
|
||||
// Make sure `name_` is always NULL-terminated, even if the source's length is
|
||||
// higher.
|
||||
name_[kCounterNameMaxLength - 1] = '\0';
|
||||
}
|
||||
|
||||
|
||||
void Counter::Enable() { enabled_ = true; }
|
||||
|
||||
|
||||
void Counter::Disable() { enabled_ = false; }
|
||||
|
||||
|
||||
bool Counter::IsEnabled() { return enabled_; }
|
||||
|
||||
|
||||
void Counter::Increment() {
|
||||
if (enabled_) {
|
||||
count_++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64_t Counter::GetCount() {
|
||||
uint64_t result = count_;
|
||||
if (type_ == Gauge) {
|
||||
// If the counter is a Gauge, reset the count after reading.
|
||||
count_ = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const char* Counter::GetName() { return name_; }
|
||||
|
||||
|
||||
CounterType Counter::GetType() { return type_; }
|
||||
|
||||
|
||||
struct CounterDescriptor {
|
||||
const char* name;
|
||||
CounterType type;
|
||||
};
|
||||
|
||||
|
||||
static const CounterDescriptor kCounterList[] =
|
||||
{{"Instruction", Cumulative},
|
||||
|
||||
{"Move Immediate", Gauge},
|
||||
{"Add/Sub DP", Gauge},
|
||||
{"Logical DP", Gauge},
|
||||
{"Other Int DP", Gauge},
|
||||
{"FP DP", Gauge},
|
||||
|
||||
{"Conditional Select", Gauge},
|
||||
{"Conditional Compare", Gauge},
|
||||
|
||||
{"Unconditional Branch", Gauge},
|
||||
{"Compare and Branch", Gauge},
|
||||
{"Test and Branch", Gauge},
|
||||
{"Conditional Branch", Gauge},
|
||||
|
||||
{"Load Integer", Gauge},
|
||||
{"Load FP", Gauge},
|
||||
{"Load Pair", Gauge},
|
||||
{"Load Literal", Gauge},
|
||||
|
||||
{"Store Integer", Gauge},
|
||||
{"Store FP", Gauge},
|
||||
{"Store Pair", Gauge},
|
||||
|
||||
{"PC Addressing", Gauge},
|
||||
{"Other", Gauge},
|
||||
{"NEON", Gauge},
|
||||
{"Crypto", Gauge}};
|
||||
|
||||
|
||||
Instrument::Instrument(const char* datafile, uint64_t sample_period)
|
||||
: output_stream_(stdout), sample_period_(sample_period) {
|
||||
// Set up the output stream. If datafile is non-NULL, use that file. If it
|
||||
// can't be opened, or datafile is NULL, use stdout.
|
||||
if (datafile != NULL) {
|
||||
output_stream_ = fopen(datafile, "w");
|
||||
if (output_stream_ == NULL) {
|
||||
printf("Can't open output file %s. Using stdout.\n", datafile);
|
||||
output_stream_ = stdout;
|
||||
}
|
||||
}
|
||||
|
||||
static const int num_counters =
|
||||
sizeof(kCounterList) / sizeof(CounterDescriptor);
|
||||
|
||||
// Dump an instrumentation description comment at the top of the file.
|
||||
fprintf(output_stream_, "# counters=%d\n", num_counters);
|
||||
fprintf(output_stream_, "# sample_period=%" PRIu64 "\n", sample_period_);
|
||||
|
||||
// Construct Counter objects from counter description array.
|
||||
for (int i = 0; i < num_counters; i++) {
|
||||
Counter* counter = new Counter(kCounterList[i].name, kCounterList[i].type);
|
||||
counters_.push_back(counter);
|
||||
}
|
||||
|
||||
DumpCounterNames();
|
||||
}
|
||||
|
||||
|
||||
Instrument::~Instrument() {
|
||||
// Dump any remaining instruction data to the output file.
|
||||
DumpCounters();
|
||||
|
||||
// Free all the counter objects.
|
||||
std::list<Counter*>::iterator it;
|
||||
for (it = counters_.begin(); it != counters_.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
|
||||
if (output_stream_ != stdout) {
|
||||
fclose(output_stream_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instrument::Update() {
|
||||
// Increment the instruction counter, and dump all counters if a sample period
|
||||
// has elapsed.
|
||||
static Counter* counter = GetCounter("Instruction");
|
||||
VIXL_ASSERT(counter->GetType() == Cumulative);
|
||||
counter->Increment();
|
||||
|
||||
if ((sample_period_ != 0) && counter->IsEnabled() &&
|
||||
(counter->GetCount() % sample_period_) == 0) {
|
||||
DumpCounters();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instrument::DumpCounters() {
|
||||
// Iterate through the counter objects, dumping their values to the output
|
||||
// stream.
|
||||
std::list<Counter*>::const_iterator it;
|
||||
for (it = counters_.begin(); it != counters_.end(); it++) {
|
||||
fprintf(output_stream_, "%" PRIu64 ",", (*it)->GetCount());
|
||||
}
|
||||
fprintf(output_stream_, "\n");
|
||||
fflush(output_stream_);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::DumpCounterNames() {
|
||||
// Iterate through the counter objects, dumping the counter names to the
|
||||
// output stream.
|
||||
std::list<Counter*>::const_iterator it;
|
||||
for (it = counters_.begin(); it != counters_.end(); it++) {
|
||||
fprintf(output_stream_, "%s,", (*it)->GetName());
|
||||
}
|
||||
fprintf(output_stream_, "\n");
|
||||
fflush(output_stream_);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::HandleInstrumentationEvent(unsigned event) {
|
||||
switch (event) {
|
||||
case InstrumentStateEnable:
|
||||
Enable();
|
||||
break;
|
||||
case InstrumentStateDisable:
|
||||
Disable();
|
||||
break;
|
||||
default:
|
||||
DumpEventMarker(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instrument::DumpEventMarker(unsigned marker) {
|
||||
// Dumpan event marker to the output stream as a specially formatted comment
|
||||
// line.
|
||||
static Counter* counter = GetCounter("Instruction");
|
||||
|
||||
fprintf(output_stream_,
|
||||
"# %c%c @ %" PRId64 "\n",
|
||||
marker & 0xff,
|
||||
(marker >> 8) & 0xff,
|
||||
counter->GetCount());
|
||||
}
|
||||
|
||||
|
||||
Counter* Instrument::GetCounter(const char* name) {
|
||||
// Get a Counter object by name from the counter list.
|
||||
std::list<Counter*>::const_iterator it;
|
||||
for (it = counters_.begin(); it != counters_.end(); it++) {
|
||||
if (strcmp((*it)->GetName(), name) == 0) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
// A Counter by that name does not exist: print an error message to stderr
|
||||
// and the output file, and exit.
|
||||
static const char* error_message =
|
||||
"# Error: Unknown counter \"%s\". Exiting.\n";
|
||||
fprintf(stderr, error_message, name);
|
||||
fprintf(output_stream_, error_message, name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::Enable() {
|
||||
std::list<Counter*>::iterator it;
|
||||
for (it = counters_.begin(); it != counters_.end(); it++) {
|
||||
(*it)->Enable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instrument::Disable() {
|
||||
std::list<Counter*>::iterator it;
|
||||
for (it = counters_.begin(); it != counters_.end(); it++) {
|
||||
(*it)->Disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitPCRelAddressing(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("PC Addressing");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitAddSubImmediate(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Add/Sub DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLogicalImmediate(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Logical DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitMoveWideImmediate(const Instruction* instr) {
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Move Immediate");
|
||||
|
||||
if (instr->IsMovn() && (instr->GetRd() == kZeroRegCode)) {
|
||||
unsigned imm = instr->GetImmMoveWide();
|
||||
HandleInstrumentationEvent(imm);
|
||||
} else {
|
||||
counter->Increment();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitBitfield(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other Int DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitExtract(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other Int DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitUnconditionalBranch(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Unconditional Branch");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitUnconditionalBranchToRegister(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Unconditional Branch");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitCompareBranch(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Compare and Branch");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitTestBranch(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Test and Branch");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitConditionalBranch(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Conditional Branch");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitSystem(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitException(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::InstrumentLoadStorePair(const Instruction* instr) {
|
||||
static Counter* load_pair_counter = GetCounter("Load Pair");
|
||||
static Counter* store_pair_counter = GetCounter("Store Pair");
|
||||
|
||||
if (instr->Mask(LoadStorePairLBit) != 0) {
|
||||
load_pair_counter->Increment();
|
||||
} else {
|
||||
store_pair_counter->Increment();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStorePairPostIndex(const Instruction* instr) {
|
||||
Update();
|
||||
InstrumentLoadStorePair(instr);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStorePairOffset(const Instruction* instr) {
|
||||
Update();
|
||||
InstrumentLoadStorePair(instr);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStorePairPreIndex(const Instruction* instr) {
|
||||
Update();
|
||||
InstrumentLoadStorePair(instr);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStorePairNonTemporal(const Instruction* instr) {
|
||||
Update();
|
||||
InstrumentLoadStorePair(instr);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStoreExclusive(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitAtomicMemory(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadLiteral(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Load Literal");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::InstrumentLoadStore(const Instruction* instr) {
|
||||
static Counter* load_int_counter = GetCounter("Load Integer");
|
||||
static Counter* store_int_counter = GetCounter("Store Integer");
|
||||
static Counter* load_fp_counter = GetCounter("Load FP");
|
||||
static Counter* store_fp_counter = GetCounter("Store FP");
|
||||
|
||||
switch (instr->Mask(LoadStoreMask)) {
|
||||
case STRB_w:
|
||||
case STRH_w:
|
||||
case STR_w:
|
||||
VIXL_FALLTHROUGH();
|
||||
case STR_x:
|
||||
store_int_counter->Increment();
|
||||
break;
|
||||
case STR_s:
|
||||
VIXL_FALLTHROUGH();
|
||||
case STR_d:
|
||||
store_fp_counter->Increment();
|
||||
break;
|
||||
case LDRB_w:
|
||||
case LDRH_w:
|
||||
case LDR_w:
|
||||
case LDR_x:
|
||||
case LDRSB_x:
|
||||
case LDRSH_x:
|
||||
case LDRSW_x:
|
||||
case LDRSB_w:
|
||||
VIXL_FALLTHROUGH();
|
||||
case LDRSH_w:
|
||||
load_int_counter->Increment();
|
||||
break;
|
||||
case LDR_s:
|
||||
VIXL_FALLTHROUGH();
|
||||
case LDR_d:
|
||||
load_fp_counter->Increment();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStoreUnscaledOffset(const Instruction* instr) {
|
||||
Update();
|
||||
InstrumentLoadStore(instr);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStorePostIndex(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
InstrumentLoadStore(instr);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStorePreIndex(const Instruction* instr) {
|
||||
Update();
|
||||
InstrumentLoadStore(instr);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStoreRegisterOffset(const Instruction* instr) {
|
||||
Update();
|
||||
InstrumentLoadStore(instr);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLoadStoreUnsignedOffset(const Instruction* instr) {
|
||||
Update();
|
||||
InstrumentLoadStore(instr);
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitLogicalShifted(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Logical DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitAddSubShifted(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Add/Sub DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitAddSubExtended(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Add/Sub DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitAddSubWithCarry(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Add/Sub DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitConditionalCompareRegister(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Conditional Compare");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitConditionalCompareImmediate(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Conditional Compare");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitConditionalSelect(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Conditional Select");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitDataProcessing1Source(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other Int DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitDataProcessing2Source(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other Int DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitDataProcessing3Source(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other Int DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitFPCompare(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("FP DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitFPConditionalCompare(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Conditional Compare");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitFPConditionalSelect(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Conditional Select");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitFPImmediate(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("FP DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitFPDataProcessing1Source(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("FP DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitFPDataProcessing2Source(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("FP DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitFPDataProcessing3Source(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("FP DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitFPIntegerConvert(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("FP DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitFPFixedPointConvert(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("FP DP");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitCrypto2RegSHA(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Crypto");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitCrypto3RegSHA(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Crypto");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitCryptoAES(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Crypto");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEON2RegMisc(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEON2RegMiscFP16(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEON3Same(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEON3SameFP16(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEON3SameExtra(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEON3Different(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONAcrossLanes(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONByIndexedElement(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONCopy(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONExtract(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONLoadStoreMultiStruct(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONLoadStoreMultiStructPostIndex(
|
||||
const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONLoadStoreSingleStruct(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONLoadStoreSingleStructPostIndex(
|
||||
const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONModifiedImmediate(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalar2RegMisc(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalar2RegMiscFP16(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalar3Diff(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalar3Same(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalar3SameFP16(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalar3SameExtra(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalarByIndexedElement(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalarCopy(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalarPairwise(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONScalarShiftImmediate(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONShiftImmediate(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONTable(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitNEONPerm(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("NEON");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitUnallocated(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
void Instrument::VisitUnimplemented(const Instruction* instr) {
|
||||
USE(instr);
|
||||
Update();
|
||||
static Counter* counter = GetCounter("Other");
|
||||
counter->Increment();
|
||||
}
|
||||
|
||||
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,528 @@
|
||||
// Copyright 2016, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "operands-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
// CPURegList utilities.
|
||||
CPURegister CPURegList::PopLowestIndex() {
|
||||
if (IsEmpty()) {
|
||||
return NoCPUReg;
|
||||
}
|
||||
int index = CountTrailingZeros(list_);
|
||||
VIXL_ASSERT((1 << index) & list_);
|
||||
Remove(index);
|
||||
return CPURegister(index, size_, type_);
|
||||
}
|
||||
|
||||
|
||||
CPURegister CPURegList::PopHighestIndex() {
|
||||
VIXL_ASSERT(IsValid());
|
||||
if (IsEmpty()) {
|
||||
return NoCPUReg;
|
||||
}
|
||||
int index = CountLeadingZeros(list_);
|
||||
index = kRegListSizeInBits - 1 - index;
|
||||
VIXL_ASSERT((1 << index) & list_);
|
||||
Remove(index);
|
||||
return CPURegister(index, size_, type_);
|
||||
}
|
||||
|
||||
|
||||
bool CPURegList::IsValid() const {
|
||||
if ((type_ == CPURegister::kRegister) || (type_ == CPURegister::kVRegister)) {
|
||||
bool is_valid = true;
|
||||
// Try to create a CPURegister for each element in the list.
|
||||
for (int i = 0; i < kRegListSizeInBits; i++) {
|
||||
if (((list_ >> i) & 1) != 0) {
|
||||
is_valid &= CPURegister(i, size_, type_).IsValid();
|
||||
}
|
||||
}
|
||||
return is_valid;
|
||||
} else if (type_ == CPURegister::kNoRegister) {
|
||||
// We can't use IsEmpty here because that asserts IsValid().
|
||||
return list_ == 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPURegList::RemoveCalleeSaved() {
|
||||
if (GetType() == CPURegister::kRegister) {
|
||||
Remove(GetCalleeSaved(GetRegisterSizeInBits()));
|
||||
} else if (GetType() == CPURegister::kVRegister) {
|
||||
Remove(GetCalleeSavedV(GetRegisterSizeInBits()));
|
||||
} else {
|
||||
VIXL_ASSERT(GetType() == CPURegister::kNoRegister);
|
||||
VIXL_ASSERT(IsEmpty());
|
||||
// The list must already be empty, so do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CPURegList CPURegList::Union(const CPURegList& list_1,
|
||||
const CPURegList& list_2,
|
||||
const CPURegList& list_3) {
|
||||
return Union(list_1, Union(list_2, list_3));
|
||||
}
|
||||
|
||||
|
||||
CPURegList CPURegList::Union(const CPURegList& list_1,
|
||||
const CPURegList& list_2,
|
||||
const CPURegList& list_3,
|
||||
const CPURegList& list_4) {
|
||||
return Union(Union(list_1, list_2), Union(list_3, list_4));
|
||||
}
|
||||
|
||||
|
||||
CPURegList CPURegList::Intersection(const CPURegList& list_1,
|
||||
const CPURegList& list_2,
|
||||
const CPURegList& list_3) {
|
||||
return Intersection(list_1, Intersection(list_2, list_3));
|
||||
}
|
||||
|
||||
|
||||
CPURegList CPURegList::Intersection(const CPURegList& list_1,
|
||||
const CPURegList& list_2,
|
||||
const CPURegList& list_3,
|
||||
const CPURegList& list_4) {
|
||||
return Intersection(Intersection(list_1, list_2),
|
||||
Intersection(list_3, list_4));
|
||||
}
|
||||
|
||||
|
||||
CPURegList CPURegList::GetCalleeSaved(unsigned size) {
|
||||
return CPURegList(CPURegister::kRegister, size, 19, 29);
|
||||
}
|
||||
|
||||
|
||||
CPURegList CPURegList::GetCalleeSavedV(unsigned size) {
|
||||
return CPURegList(CPURegister::kVRegister, size, 8, 15);
|
||||
}
|
||||
|
||||
|
||||
CPURegList CPURegList::GetCallerSaved(unsigned size) {
|
||||
// Registers x0-x18 and lr (x30) are caller-saved.
|
||||
CPURegList list = CPURegList(CPURegister::kRegister, size, 0, 18);
|
||||
// Do not use lr directly to avoid initialisation order fiasco bugs for users.
|
||||
list.Combine(Register(30, kXRegSize));
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
CPURegList CPURegList::GetCallerSavedV(unsigned size) {
|
||||
// Registers d0-d7 and d16-d31 are caller-saved.
|
||||
CPURegList list = CPURegList(CPURegister::kVRegister, size, 0, 7);
|
||||
list.Combine(CPURegList(CPURegister::kVRegister, size, 16, 31));
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
const CPURegList kCalleeSaved = CPURegList::GetCalleeSaved();
|
||||
const CPURegList kCalleeSavedV = CPURegList::GetCalleeSavedV();
|
||||
const CPURegList kCallerSaved = CPURegList::GetCallerSaved();
|
||||
const CPURegList kCallerSavedV = CPURegList::GetCallerSavedV();
|
||||
|
||||
|
||||
// Registers.
|
||||
#define WREG(n) w##n,
|
||||
const Register Register::wregisters[] = {AARCH64_REGISTER_CODE_LIST(WREG)};
|
||||
#undef WREG
|
||||
|
||||
#define XREG(n) x##n,
|
||||
const Register Register::xregisters[] = {AARCH64_REGISTER_CODE_LIST(XREG)};
|
||||
#undef XREG
|
||||
|
||||
#define BREG(n) b##n,
|
||||
const VRegister VRegister::bregisters[] = {AARCH64_REGISTER_CODE_LIST(BREG)};
|
||||
#undef BREG
|
||||
|
||||
#define HREG(n) h##n,
|
||||
const VRegister VRegister::hregisters[] = {AARCH64_REGISTER_CODE_LIST(HREG)};
|
||||
#undef HREG
|
||||
|
||||
#define SREG(n) s##n,
|
||||
const VRegister VRegister::sregisters[] = {AARCH64_REGISTER_CODE_LIST(SREG)};
|
||||
#undef SREG
|
||||
|
||||
#define DREG(n) d##n,
|
||||
const VRegister VRegister::dregisters[] = {AARCH64_REGISTER_CODE_LIST(DREG)};
|
||||
#undef DREG
|
||||
|
||||
#define QREG(n) q##n,
|
||||
const VRegister VRegister::qregisters[] = {AARCH64_REGISTER_CODE_LIST(QREG)};
|
||||
#undef QREG
|
||||
|
||||
#define VREG(n) v##n,
|
||||
const VRegister VRegister::vregisters[] = {AARCH64_REGISTER_CODE_LIST(VREG)};
|
||||
#undef VREG
|
||||
|
||||
|
||||
const Register& Register::GetWRegFromCode(unsigned code) {
|
||||
if (code == kSPRegInternalCode) {
|
||||
return wsp;
|
||||
} else {
|
||||
VIXL_ASSERT(code < kNumberOfRegisters);
|
||||
return wregisters[code];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Register& Register::GetXRegFromCode(unsigned code) {
|
||||
if (code == kSPRegInternalCode) {
|
||||
return sp;
|
||||
} else {
|
||||
VIXL_ASSERT(code < kNumberOfRegisters);
|
||||
return xregisters[code];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const VRegister& VRegister::GetBRegFromCode(unsigned code) {
|
||||
VIXL_ASSERT(code < kNumberOfVRegisters);
|
||||
return bregisters[code];
|
||||
}
|
||||
|
||||
|
||||
const VRegister& VRegister::GetHRegFromCode(unsigned code) {
|
||||
VIXL_ASSERT(code < kNumberOfVRegisters);
|
||||
return hregisters[code];
|
||||
}
|
||||
|
||||
|
||||
const VRegister& VRegister::GetSRegFromCode(unsigned code) {
|
||||
VIXL_ASSERT(code < kNumberOfVRegisters);
|
||||
return sregisters[code];
|
||||
}
|
||||
|
||||
|
||||
const VRegister& VRegister::GetDRegFromCode(unsigned code) {
|
||||
VIXL_ASSERT(code < kNumberOfVRegisters);
|
||||
return dregisters[code];
|
||||
}
|
||||
|
||||
|
||||
const VRegister& VRegister::GetQRegFromCode(unsigned code) {
|
||||
VIXL_ASSERT(code < kNumberOfVRegisters);
|
||||
return qregisters[code];
|
||||
}
|
||||
|
||||
|
||||
const VRegister& VRegister::GetVRegFromCode(unsigned code) {
|
||||
VIXL_ASSERT(code < kNumberOfVRegisters);
|
||||
return vregisters[code];
|
||||
}
|
||||
|
||||
|
||||
const Register& CPURegister::W() const {
|
||||
VIXL_ASSERT(IsValidRegister());
|
||||
return Register::GetWRegFromCode(code_);
|
||||
}
|
||||
|
||||
|
||||
const Register& CPURegister::X() const {
|
||||
VIXL_ASSERT(IsValidRegister());
|
||||
return Register::GetXRegFromCode(code_);
|
||||
}
|
||||
|
||||
|
||||
const VRegister& CPURegister::B() const {
|
||||
VIXL_ASSERT(IsValidVRegister());
|
||||
return VRegister::GetBRegFromCode(code_);
|
||||
}
|
||||
|
||||
|
||||
const VRegister& CPURegister::H() const {
|
||||
VIXL_ASSERT(IsValidVRegister());
|
||||
return VRegister::GetHRegFromCode(code_);
|
||||
}
|
||||
|
||||
|
||||
const VRegister& CPURegister::S() const {
|
||||
VIXL_ASSERT(IsValidVRegister());
|
||||
return VRegister::GetSRegFromCode(code_);
|
||||
}
|
||||
|
||||
|
||||
const VRegister& CPURegister::D() const {
|
||||
VIXL_ASSERT(IsValidVRegister());
|
||||
return VRegister::GetDRegFromCode(code_);
|
||||
}
|
||||
|
||||
|
||||
const VRegister& CPURegister::Q() const {
|
||||
VIXL_ASSERT(IsValidVRegister());
|
||||
return VRegister::GetQRegFromCode(code_);
|
||||
}
|
||||
|
||||
|
||||
const VRegister& CPURegister::V() const {
|
||||
VIXL_ASSERT(IsValidVRegister());
|
||||
return VRegister::GetVRegFromCode(code_);
|
||||
}
|
||||
|
||||
|
||||
// Operand.
|
||||
Operand::Operand(int64_t immediate)
|
||||
: immediate_(immediate),
|
||||
reg_(NoReg),
|
||||
shift_(NO_SHIFT),
|
||||
extend_(NO_EXTEND),
|
||||
shift_amount_(0) {}
|
||||
|
||||
|
||||
Operand::Operand(Register reg, Shift shift, unsigned shift_amount)
|
||||
: reg_(reg),
|
||||
shift_(shift),
|
||||
extend_(NO_EXTEND),
|
||||
shift_amount_(shift_amount) {
|
||||
VIXL_ASSERT(shift != MSL);
|
||||
VIXL_ASSERT(reg.Is64Bits() || (shift_amount < kWRegSize));
|
||||
VIXL_ASSERT(reg.Is32Bits() || (shift_amount < kXRegSize));
|
||||
VIXL_ASSERT(!reg.IsSP());
|
||||
}
|
||||
|
||||
|
||||
Operand::Operand(Register reg, Extend extend, unsigned shift_amount)
|
||||
: reg_(reg),
|
||||
shift_(NO_SHIFT),
|
||||
extend_(extend),
|
||||
shift_amount_(shift_amount) {
|
||||
VIXL_ASSERT(reg.IsValid());
|
||||
VIXL_ASSERT(shift_amount <= 4);
|
||||
VIXL_ASSERT(!reg.IsSP());
|
||||
|
||||
// Extend modes SXTX and UXTX require a 64-bit register.
|
||||
VIXL_ASSERT(reg.Is64Bits() || ((extend != SXTX) && (extend != UXTX)));
|
||||
}
|
||||
|
||||
|
||||
bool Operand::IsImmediate() const { return reg_.Is(NoReg); }
|
||||
|
||||
|
||||
bool Operand::IsPlainRegister() const {
|
||||
return reg_.IsValid() &&
|
||||
(((shift_ == NO_SHIFT) && (extend_ == NO_EXTEND)) ||
|
||||
// No-op shifts.
|
||||
((shift_ != NO_SHIFT) && (shift_amount_ == 0)) ||
|
||||
// No-op extend operations.
|
||||
// We can't include [US]XTW here without knowing more about the
|
||||
// context; they are only no-ops for 32-bit operations.
|
||||
//
|
||||
// For example, this operand could be replaced with w1:
|
||||
// __ Add(w0, w0, Operand(w1, UXTW));
|
||||
// However, no plain register can replace it in this context:
|
||||
// __ Add(x0, x0, Operand(w1, UXTW));
|
||||
(((extend_ == UXTX) || (extend_ == SXTX)) && (shift_amount_ == 0)));
|
||||
}
|
||||
|
||||
|
||||
bool Operand::IsShiftedRegister() const {
|
||||
return reg_.IsValid() && (shift_ != NO_SHIFT);
|
||||
}
|
||||
|
||||
|
||||
bool Operand::IsExtendedRegister() const {
|
||||
return reg_.IsValid() && (extend_ != NO_EXTEND);
|
||||
}
|
||||
|
||||
|
||||
bool Operand::IsZero() const {
|
||||
if (IsImmediate()) {
|
||||
return GetImmediate() == 0;
|
||||
} else {
|
||||
return GetRegister().IsZero();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Operand Operand::ToExtendedRegister() const {
|
||||
VIXL_ASSERT(IsShiftedRegister());
|
||||
VIXL_ASSERT((shift_ == LSL) && (shift_amount_ <= 4));
|
||||
return Operand(reg_, reg_.Is64Bits() ? UXTX : UXTW, shift_amount_);
|
||||
}
|
||||
|
||||
|
||||
// MemOperand
|
||||
MemOperand::MemOperand()
|
||||
: base_(NoReg),
|
||||
regoffset_(NoReg),
|
||||
offset_(0),
|
||||
addrmode_(Offset),
|
||||
shift_(NO_SHIFT),
|
||||
extend_(NO_EXTEND) {}
|
||||
|
||||
|
||||
MemOperand::MemOperand(Register base, int64_t offset, AddrMode addrmode)
|
||||
: base_(base),
|
||||
regoffset_(NoReg),
|
||||
offset_(offset),
|
||||
addrmode_(addrmode),
|
||||
shift_(NO_SHIFT),
|
||||
extend_(NO_EXTEND),
|
||||
shift_amount_(0) {
|
||||
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
|
||||
}
|
||||
|
||||
|
||||
MemOperand::MemOperand(Register base,
|
||||
Register regoffset,
|
||||
Extend extend,
|
||||
unsigned shift_amount)
|
||||
: base_(base),
|
||||
regoffset_(regoffset),
|
||||
offset_(0),
|
||||
addrmode_(Offset),
|
||||
shift_(NO_SHIFT),
|
||||
extend_(extend),
|
||||
shift_amount_(shift_amount) {
|
||||
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
|
||||
VIXL_ASSERT(!regoffset.IsSP());
|
||||
VIXL_ASSERT((extend == UXTW) || (extend == SXTW) || (extend == SXTX));
|
||||
|
||||
// SXTX extend mode requires a 64-bit offset register.
|
||||
VIXL_ASSERT(regoffset.Is64Bits() || (extend != SXTX));
|
||||
}
|
||||
|
||||
|
||||
MemOperand::MemOperand(Register base,
|
||||
Register regoffset,
|
||||
Shift shift,
|
||||
unsigned shift_amount)
|
||||
: base_(base),
|
||||
regoffset_(regoffset),
|
||||
offset_(0),
|
||||
addrmode_(Offset),
|
||||
shift_(shift),
|
||||
extend_(NO_EXTEND),
|
||||
shift_amount_(shift_amount) {
|
||||
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
|
||||
VIXL_ASSERT(regoffset.Is64Bits() && !regoffset.IsSP());
|
||||
VIXL_ASSERT(shift == LSL);
|
||||
}
|
||||
|
||||
|
||||
MemOperand::MemOperand(Register base, const Operand& offset, AddrMode addrmode)
|
||||
: base_(base),
|
||||
regoffset_(NoReg),
|
||||
addrmode_(addrmode),
|
||||
shift_(NO_SHIFT),
|
||||
extend_(NO_EXTEND),
|
||||
shift_amount_(0) {
|
||||
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
|
||||
|
||||
if (offset.IsImmediate()) {
|
||||
offset_ = offset.GetImmediate();
|
||||
} else if (offset.IsShiftedRegister()) {
|
||||
VIXL_ASSERT((addrmode == Offset) || (addrmode == PostIndex));
|
||||
|
||||
regoffset_ = offset.GetRegister();
|
||||
shift_ = offset.GetShift();
|
||||
shift_amount_ = offset.GetShiftAmount();
|
||||
|
||||
extend_ = NO_EXTEND;
|
||||
offset_ = 0;
|
||||
|
||||
// These assertions match those in the shifted-register constructor.
|
||||
VIXL_ASSERT(regoffset_.Is64Bits() && !regoffset_.IsSP());
|
||||
VIXL_ASSERT(shift_ == LSL);
|
||||
} else {
|
||||
VIXL_ASSERT(offset.IsExtendedRegister());
|
||||
VIXL_ASSERT(addrmode == Offset);
|
||||
|
||||
regoffset_ = offset.GetRegister();
|
||||
extend_ = offset.GetExtend();
|
||||
shift_amount_ = offset.GetShiftAmount();
|
||||
|
||||
shift_ = NO_SHIFT;
|
||||
offset_ = 0;
|
||||
|
||||
// These assertions match those in the extended-register constructor.
|
||||
VIXL_ASSERT(!regoffset_.IsSP());
|
||||
VIXL_ASSERT((extend_ == UXTW) || (extend_ == SXTW) || (extend_ == SXTX));
|
||||
VIXL_ASSERT((regoffset_.Is64Bits() || (extend_ != SXTX)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MemOperand::IsImmediateOffset() const {
|
||||
return (addrmode_ == Offset) && regoffset_.Is(NoReg);
|
||||
}
|
||||
|
||||
|
||||
bool MemOperand::IsRegisterOffset() const {
|
||||
return (addrmode_ == Offset) && !regoffset_.Is(NoReg);
|
||||
}
|
||||
|
||||
|
||||
bool MemOperand::IsPreIndex() const { return addrmode_ == PreIndex; }
|
||||
|
||||
|
||||
bool MemOperand::IsPostIndex() const { return addrmode_ == PostIndex; }
|
||||
|
||||
|
||||
void MemOperand::AddOffset(int64_t offset) {
|
||||
VIXL_ASSERT(IsImmediateOffset());
|
||||
offset_ += offset;
|
||||
}
|
||||
|
||||
|
||||
GenericOperand::GenericOperand(const CPURegister& reg)
|
||||
: cpu_register_(reg), mem_op_size_(0) {
|
||||
if (reg.IsQ()) {
|
||||
VIXL_ASSERT(reg.GetSizeInBits() > static_cast<int>(kXRegSize));
|
||||
// Support for Q registers is not implemented yet.
|
||||
VIXL_UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GenericOperand::GenericOperand(const MemOperand& mem_op, size_t mem_op_size)
|
||||
: cpu_register_(NoReg), mem_op_(mem_op), mem_op_size_(mem_op_size) {
|
||||
if (mem_op_size_ > kXRegSizeInBytes) {
|
||||
// We only support generic operands up to the size of X registers.
|
||||
VIXL_UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
|
||||
bool GenericOperand::Equals(const GenericOperand& other) const {
|
||||
if (!IsValid() || !other.IsValid()) {
|
||||
// Two invalid generic operands are considered equal.
|
||||
return !IsValid() && !other.IsValid();
|
||||
}
|
||||
if (IsCPURegister() && other.IsCPURegister()) {
|
||||
return GetCPURegister().Is(other.GetCPURegister());
|
||||
} else if (IsMemOperand() && other.IsMemOperand()) {
|
||||
return GetMemOperand().Equals(other.GetMemOperand()) &&
|
||||
(GetMemOperandSizeInBytes() == other.GetMemOperandSizeInBytes());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace vixl::aarch64
|
||||
@@ -0,0 +1,197 @@
|
||||
// Copyright 2018, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
|
||||
#include "simulator-aarch64.h"
|
||||
|
||||
#include "utils-vixl.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
// Randomly generated example keys for simulating only.
|
||||
const Simulator::PACKey Simulator::kPACKeyIA = {0xc31718727de20f71,
|
||||
0xab9fd4e14b2fec51,
|
||||
0};
|
||||
const Simulator::PACKey Simulator::kPACKeyIB = {0xeebb163b474e04c8,
|
||||
0x5267ac6fc280fb7c,
|
||||
1};
|
||||
const Simulator::PACKey Simulator::kPACKeyDA = {0x5caef808deb8b1e2,
|
||||
0xd347cbc06b7b0f77,
|
||||
0};
|
||||
const Simulator::PACKey Simulator::kPACKeyDB = {0xe06aa1a949ba8cc7,
|
||||
0xcfde69e3db6d0432,
|
||||
1};
|
||||
|
||||
// The general PAC key isn't intended to be used with AuthPAC so we ensure the
|
||||
// key number is invalid and asserts if used incorrectly.
|
||||
const Simulator::PACKey Simulator::kPACKeyGA = {0xfcd98a44d564b3d5,
|
||||
0x6c56df1904bf0ddc,
|
||||
-1};
|
||||
|
||||
static uint64_t GetNibble(uint64_t in_data, int position) {
|
||||
return (in_data >> position) & 0xf;
|
||||
}
|
||||
|
||||
static uint64_t ShuffleNibbles(uint64_t in_data) {
|
||||
static int in_positions[16] =
|
||||
{4, 36, 52, 40, 44, 0, 24, 12, 56, 60, 8, 32, 16, 28, 20, 48};
|
||||
uint64_t out_data = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
out_data |= GetNibble(in_data, in_positions[i]) << (4 * i);
|
||||
}
|
||||
return out_data;
|
||||
}
|
||||
|
||||
static uint64_t SubstituteNibbles(uint64_t in_data) {
|
||||
// Randomly chosen substitutes.
|
||||
static uint64_t subs[16] =
|
||||
{4, 7, 3, 9, 10, 14, 0, 1, 15, 2, 8, 6, 12, 5, 11, 13};
|
||||
uint64_t out_data = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int index = (in_data >> (4 * i)) & 0xf;
|
||||
out_data |= subs[index] << (4 * i);
|
||||
}
|
||||
return out_data;
|
||||
}
|
||||
|
||||
// Rotate nibble to the left by the amount specified.
|
||||
static uint64_t RotNibble(uint64_t in_cell, int amount) {
|
||||
VIXL_ASSERT((amount >= 0) && (amount <= 3));
|
||||
|
||||
in_cell &= 0xf;
|
||||
uint64_t temp = (in_cell << 4) | in_cell;
|
||||
return (temp >> (4 - amount)) & 0xf;
|
||||
}
|
||||
|
||||
static uint64_t BigShuffle(uint64_t in_data) {
|
||||
uint64_t out_data = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint64_t n12 = GetNibble(in_data, 4 * (i + 12));
|
||||
uint64_t n8 = GetNibble(in_data, 4 * (i + 8));
|
||||
uint64_t n4 = GetNibble(in_data, 4 * (i + 4));
|
||||
uint64_t n0 = GetNibble(in_data, 4 * (i + 0));
|
||||
|
||||
uint64_t t0 = RotNibble(n8, 2) ^ RotNibble(n4, 1) ^ RotNibble(n0, 1);
|
||||
uint64_t t1 = RotNibble(n12, 1) ^ RotNibble(n4, 2) ^ RotNibble(n0, 1);
|
||||
uint64_t t2 = RotNibble(n12, 2) ^ RotNibble(n8, 1) ^ RotNibble(n0, 1);
|
||||
uint64_t t3 = RotNibble(n12, 1) ^ RotNibble(n8, 1) ^ RotNibble(n4, 2);
|
||||
|
||||
out_data |= t3 << (4 * (i + 0));
|
||||
out_data |= t2 << (4 * (i + 4));
|
||||
out_data |= t1 << (4 * (i + 8));
|
||||
out_data |= t0 << (4 * (i + 12));
|
||||
}
|
||||
return out_data;
|
||||
}
|
||||
|
||||
// A simple, non-standard hash function invented for simulating. It mixes
|
||||
// reasonably well, however it is unlikely to be cryptographically secure and
|
||||
// may have a higher collision chance than other hashing algorithms.
|
||||
uint64_t Simulator::ComputePAC(uint64_t data, uint64_t context, PACKey key) {
|
||||
uint64_t working_value = data ^ key.high;
|
||||
working_value = BigShuffle(working_value);
|
||||
working_value = ShuffleNibbles(working_value);
|
||||
working_value ^= key.low;
|
||||
working_value = ShuffleNibbles(working_value);
|
||||
working_value = BigShuffle(working_value);
|
||||
working_value ^= context;
|
||||
working_value = SubstituteNibbles(working_value);
|
||||
working_value = BigShuffle(working_value);
|
||||
working_value = SubstituteNibbles(working_value);
|
||||
|
||||
return working_value;
|
||||
}
|
||||
|
||||
// The TTBR is selected by bit 63 or 55 depending on TBI for pointers without
|
||||
// codes, but is always 55 once a PAC code is added to a pointer. For this
|
||||
// reason, it must be calculated at the call site.
|
||||
uint64_t Simulator::CalculatePACMask(uint64_t ptr, PointerType type, int ttbr) {
|
||||
int bottom_pac_bit = GetBottomPACBit(ptr, ttbr);
|
||||
int top_pac_bit = GetTopPACBit(ptr, type);
|
||||
return ExtractUnsignedBitfield64(top_pac_bit,
|
||||
bottom_pac_bit,
|
||||
0xffffffffffffffff & ~kTTBRMask)
|
||||
<< bottom_pac_bit;
|
||||
}
|
||||
|
||||
uint64_t Simulator::AuthPAC(uint64_t ptr,
|
||||
uint64_t context,
|
||||
PACKey key,
|
||||
PointerType type) {
|
||||
VIXL_ASSERT((key.number == 0) || (key.number == 1));
|
||||
|
||||
uint64_t pac_mask = CalculatePACMask(ptr, type, (ptr >> 55) & 1);
|
||||
uint64_t original_ptr =
|
||||
((ptr & kTTBRMask) == 0) ? (ptr & ~pac_mask) : (ptr | pac_mask);
|
||||
|
||||
uint64_t pac = ComputePAC(original_ptr, context, key);
|
||||
|
||||
uint64_t error_code = 1 << key.number;
|
||||
if ((pac & pac_mask) == (ptr & pac_mask)) {
|
||||
return original_ptr;
|
||||
} else {
|
||||
int error_lsb = GetTopPACBit(ptr, type) - 2;
|
||||
uint64_t error_mask = UINT64_C(0x3) << error_lsb;
|
||||
return (original_ptr & ~error_mask) | (error_code << error_lsb);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Simulator::AddPAC(uint64_t ptr,
|
||||
uint64_t context,
|
||||
PACKey key,
|
||||
PointerType type) {
|
||||
int top_pac_bit = GetTopPACBit(ptr, type);
|
||||
|
||||
// TODO: Properly handle the case where extension bits are bad and TBI is
|
||||
// turned off, and also test me.
|
||||
VIXL_ASSERT(HasTBI(ptr, type));
|
||||
int ttbr = (ptr >> 55) & 1;
|
||||
uint64_t pac_mask = CalculatePACMask(ptr, type, ttbr);
|
||||
uint64_t ext_ptr = (ttbr == 0) ? (ptr & ~pac_mask) : (ptr | pac_mask);
|
||||
|
||||
uint64_t pac = ComputePAC(ext_ptr, context, key);
|
||||
|
||||
// If the pointer isn't all zeroes or all ones in the PAC bitfield, corrupt
|
||||
// the resulting code.
|
||||
if (((ptr & (pac_mask | kTTBRMask)) != 0x0) &&
|
||||
((~ptr & (pac_mask | kTTBRMask)) != 0x0)) {
|
||||
pac ^= UINT64_C(1) << (top_pac_bit - 1);
|
||||
}
|
||||
|
||||
uint64_t ttbr_shifted = static_cast<uint64_t>(ttbr) << 55;
|
||||
return (pac & pac_mask) | ttbr_shifted | (ptr & ~pac_mask);
|
||||
}
|
||||
|
||||
uint64_t Simulator::StripPAC(uint64_t ptr, PointerType type) {
|
||||
uint64_t pac_mask = CalculatePACMask(ptr, type, (ptr >> 55) & 1);
|
||||
return ((ptr & kTTBRMask) == 0) ? (ptr & ~pac_mask) : (ptr | pac_mask);
|
||||
}
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,178 @@
|
||||
// Copyright 2017, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
extern "C" {
|
||||
#include <sys/mman.h>
|
||||
}
|
||||
|
||||
#include "code-buffer-vixl.h"
|
||||
#include "utils-vixl.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
|
||||
CodeBuffer::CodeBuffer(size_t capacity)
|
||||
: buffer_(NULL),
|
||||
managed_(true),
|
||||
cursor_(NULL),
|
||||
dirty_(false),
|
||||
capacity_(capacity) {
|
||||
if (capacity_ == 0) {
|
||||
return;
|
||||
}
|
||||
#ifdef VIXL_CODE_BUFFER_MALLOC
|
||||
buffer_ = reinterpret_cast<byte*>(malloc(capacity_));
|
||||
#elif defined(VIXL_CODE_BUFFER_MMAP)
|
||||
buffer_ = reinterpret_cast<byte*>(mmap(NULL,
|
||||
capacity,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1,
|
||||
0));
|
||||
#else
|
||||
#error Unknown code buffer allocator.
|
||||
#endif
|
||||
VIXL_CHECK(buffer_ != NULL);
|
||||
// Aarch64 instructions must be word aligned, we assert the default allocator
|
||||
// always returns word align memory.
|
||||
VIXL_ASSERT(IsWordAligned(buffer_));
|
||||
|
||||
cursor_ = buffer_;
|
||||
}
|
||||
|
||||
|
||||
CodeBuffer::CodeBuffer(byte* buffer, size_t capacity)
|
||||
: buffer_(reinterpret_cast<byte*>(buffer)),
|
||||
managed_(false),
|
||||
cursor_(reinterpret_cast<byte*>(buffer)),
|
||||
dirty_(false),
|
||||
capacity_(capacity) {
|
||||
VIXL_ASSERT(buffer_ != NULL);
|
||||
}
|
||||
|
||||
|
||||
CodeBuffer::~CodeBuffer() {
|
||||
VIXL_ASSERT(!IsDirty());
|
||||
if (managed_) {
|
||||
#ifdef VIXL_CODE_BUFFER_MALLOC
|
||||
free(buffer_);
|
||||
#elif defined(VIXL_CODE_BUFFER_MMAP)
|
||||
munmap(buffer_, capacity_);
|
||||
#else
|
||||
#error Unknown code buffer allocator.
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef VIXL_CODE_BUFFER_MMAP
|
||||
void CodeBuffer::SetExecutable() {
|
||||
int ret = mprotect(buffer_, capacity_, PROT_READ | PROT_EXEC);
|
||||
VIXL_CHECK(ret == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef VIXL_CODE_BUFFER_MMAP
|
||||
void CodeBuffer::SetWritable() {
|
||||
int ret = mprotect(buffer_, capacity_, PROT_READ | PROT_WRITE);
|
||||
VIXL_CHECK(ret == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void CodeBuffer::EmitString(const char* string) {
|
||||
VIXL_ASSERT(HasSpaceFor(strlen(string) + 1));
|
||||
char* dst = reinterpret_cast<char*>(cursor_);
|
||||
dirty_ = true;
|
||||
char* null_char = stpcpy(dst, string);
|
||||
cursor_ = reinterpret_cast<byte*>(null_char) + 1;
|
||||
}
|
||||
|
||||
|
||||
void CodeBuffer::EmitData(const void* data, size_t size) {
|
||||
VIXL_ASSERT(HasSpaceFor(size));
|
||||
dirty_ = true;
|
||||
memcpy(cursor_, data, size);
|
||||
cursor_ = cursor_ + size;
|
||||
}
|
||||
|
||||
|
||||
void CodeBuffer::UpdateData(size_t offset, const void* data, size_t size) {
|
||||
dirty_ = true;
|
||||
byte* dst = buffer_ + offset;
|
||||
VIXL_ASSERT(dst + size <= cursor_);
|
||||
memcpy(dst, data, size);
|
||||
}
|
||||
|
||||
|
||||
void CodeBuffer::Align() {
|
||||
byte* end = AlignUp(cursor_, 4);
|
||||
const size_t padding_size = end - cursor_;
|
||||
VIXL_ASSERT(padding_size <= 4);
|
||||
EmitZeroedBytes(static_cast<int>(padding_size));
|
||||
}
|
||||
|
||||
void CodeBuffer::EmitZeroedBytes(int n) {
|
||||
EnsureSpaceFor(n);
|
||||
dirty_ = true;
|
||||
memset(cursor_, 0, n);
|
||||
cursor_ += n;
|
||||
}
|
||||
|
||||
void CodeBuffer::Reset() {
|
||||
#ifdef VIXL_DEBUG
|
||||
if (managed_) {
|
||||
// Fill with zeros (there is no useful value common to A32 and T32).
|
||||
memset(buffer_, 0, capacity_);
|
||||
}
|
||||
#endif
|
||||
cursor_ = buffer_;
|
||||
SetClean();
|
||||
}
|
||||
|
||||
|
||||
void CodeBuffer::Grow(size_t new_capacity) {
|
||||
VIXL_ASSERT(managed_);
|
||||
VIXL_ASSERT(new_capacity > capacity_);
|
||||
ptrdiff_t cursor_offset = GetCursorOffset();
|
||||
#ifdef VIXL_CODE_BUFFER_MALLOC
|
||||
buffer_ = static_cast<byte*>(realloc(buffer_, new_capacity));
|
||||
VIXL_CHECK(buffer_ != NULL);
|
||||
#elif defined(VIXL_CODE_BUFFER_MMAP)
|
||||
buffer_ = static_cast<byte*>(
|
||||
mremap(buffer_, capacity_, new_capacity, MREMAP_MAYMOVE));
|
||||
VIXL_CHECK(buffer_ != MAP_FAILED);
|
||||
#else
|
||||
#error Unknown code buffer allocator.
|
||||
#endif
|
||||
|
||||
cursor_ = buffer_ + cursor_offset;
|
||||
capacity_ = new_capacity;
|
||||
}
|
||||
|
||||
|
||||
} // namespace vixl
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright 2015, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "compiler-intrinsics-vixl.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
|
||||
int CountLeadingSignBitsFallBack(int64_t value, int width) {
|
||||
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
|
||||
if (value >= 0) {
|
||||
return CountLeadingZeros(value, width) - 1;
|
||||
} else {
|
||||
return CountLeadingZeros(~value, width) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CountLeadingZerosFallBack(uint64_t value, int width) {
|
||||
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
|
||||
if (value == 0) {
|
||||
return width;
|
||||
}
|
||||
int count = 0;
|
||||
value = value << (64 - width);
|
||||
if ((value & UINT64_C(0xffffffff00000000)) == 0) {
|
||||
count += 32;
|
||||
value = value << 32;
|
||||
}
|
||||
if ((value & UINT64_C(0xffff000000000000)) == 0) {
|
||||
count += 16;
|
||||
value = value << 16;
|
||||
}
|
||||
if ((value & UINT64_C(0xff00000000000000)) == 0) {
|
||||
count += 8;
|
||||
value = value << 8;
|
||||
}
|
||||
if ((value & UINT64_C(0xf000000000000000)) == 0) {
|
||||
count += 4;
|
||||
value = value << 4;
|
||||
}
|
||||
if ((value & UINT64_C(0xc000000000000000)) == 0) {
|
||||
count += 2;
|
||||
value = value << 2;
|
||||
}
|
||||
if ((value & UINT64_C(0x8000000000000000)) == 0) {
|
||||
count += 1;
|
||||
}
|
||||
count += (value == 0);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int CountSetBitsFallBack(uint64_t value, int width) {
|
||||
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
|
||||
|
||||
// Mask out unused bits to ensure that they are not counted.
|
||||
value &= (UINT64_C(0xffffffffffffffff) >> (64 - width));
|
||||
|
||||
// Add up the set bits.
|
||||
// The algorithm works by adding pairs of bit fields together iteratively,
|
||||
// where the size of each bit field doubles each time.
|
||||
// An example for an 8-bit value:
|
||||
// Bits: h g f e d c b a
|
||||
// \ | \ | \ | \ |
|
||||
// value = h+g f+e d+c b+a
|
||||
// \ | \ |
|
||||
// value = h+g+f+e d+c+b+a
|
||||
// \ |
|
||||
// value = h+g+f+e+d+c+b+a
|
||||
const uint64_t kMasks[] = {
|
||||
UINT64_C(0x5555555555555555),
|
||||
UINT64_C(0x3333333333333333),
|
||||
UINT64_C(0x0f0f0f0f0f0f0f0f),
|
||||
UINT64_C(0x00ff00ff00ff00ff),
|
||||
UINT64_C(0x0000ffff0000ffff),
|
||||
UINT64_C(0x00000000ffffffff),
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < (sizeof(kMasks) / sizeof(kMasks[0])); i++) {
|
||||
int shift = 1 << i;
|
||||
value = ((value >> shift) & kMasks[i]) + (value & kMasks[i]);
|
||||
}
|
||||
|
||||
return static_cast<int>(value);
|
||||
}
|
||||
|
||||
|
||||
int CountTrailingZerosFallBack(uint64_t value, int width) {
|
||||
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
|
||||
int count = 0;
|
||||
value = value << (64 - width);
|
||||
if ((value & UINT64_C(0xffffffff)) == 0) {
|
||||
count += 32;
|
||||
value = value >> 32;
|
||||
}
|
||||
if ((value & 0xffff) == 0) {
|
||||
count += 16;
|
||||
value = value >> 16;
|
||||
}
|
||||
if ((value & 0xff) == 0) {
|
||||
count += 8;
|
||||
value = value >> 8;
|
||||
}
|
||||
if ((value & 0xf) == 0) {
|
||||
count += 4;
|
||||
value = value >> 4;
|
||||
}
|
||||
if ((value & 0x3) == 0) {
|
||||
count += 2;
|
||||
value = value >> 2;
|
||||
}
|
||||
if ((value & 0x1) == 0) {
|
||||
count += 1;
|
||||
}
|
||||
count += (value == 0);
|
||||
return count - (64 - width);
|
||||
}
|
||||
|
||||
|
||||
} // namespace vixl
|
||||
@@ -0,0 +1,211 @@
|
||||
// Copyright 2018, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "cpu-features.h"
|
||||
#include "globals-vixl.h"
|
||||
#include "utils-vixl.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
static uint64_t MakeFeatureMask(CPUFeatures::Feature feature) {
|
||||
if (feature == CPUFeatures::kNone) {
|
||||
return 0;
|
||||
} else {
|
||||
// Check that the shift is well-defined, and that the feature is valid.
|
||||
VIXL_STATIC_ASSERT(CPUFeatures::kNumberOfFeatures <=
|
||||
(sizeof(uint64_t) * 8));
|
||||
VIXL_ASSERT(feature < CPUFeatures::kNumberOfFeatures);
|
||||
return UINT64_C(1) << feature;
|
||||
}
|
||||
}
|
||||
|
||||
CPUFeatures::CPUFeatures(Feature feature0,
|
||||
Feature feature1,
|
||||
Feature feature2,
|
||||
Feature feature3)
|
||||
: features_(0) {
|
||||
Combine(feature0, feature1, feature2, feature3);
|
||||
}
|
||||
|
||||
CPUFeatures CPUFeatures::All() {
|
||||
CPUFeatures all;
|
||||
// Check that the shift is well-defined.
|
||||
VIXL_STATIC_ASSERT(CPUFeatures::kNumberOfFeatures < (sizeof(uint64_t) * 8));
|
||||
all.features_ = (UINT64_C(1) << kNumberOfFeatures) - 1;
|
||||
return all;
|
||||
}
|
||||
|
||||
CPUFeatures CPUFeatures::InferFromOS() {
|
||||
// TODO: Actually infer features from the OS.
|
||||
return CPUFeatures();
|
||||
}
|
||||
|
||||
void CPUFeatures::Combine(const CPUFeatures& other) {
|
||||
features_ |= other.features_;
|
||||
}
|
||||
|
||||
void CPUFeatures::Combine(Feature feature0,
|
||||
Feature feature1,
|
||||
Feature feature2,
|
||||
Feature feature3) {
|
||||
features_ |= MakeFeatureMask(feature0);
|
||||
features_ |= MakeFeatureMask(feature1);
|
||||
features_ |= MakeFeatureMask(feature2);
|
||||
features_ |= MakeFeatureMask(feature3);
|
||||
}
|
||||
|
||||
void CPUFeatures::Remove(const CPUFeatures& other) {
|
||||
features_ &= ~other.features_;
|
||||
}
|
||||
|
||||
void CPUFeatures::Remove(Feature feature0,
|
||||
Feature feature1,
|
||||
Feature feature2,
|
||||
Feature feature3) {
|
||||
features_ &= ~MakeFeatureMask(feature0);
|
||||
features_ &= ~MakeFeatureMask(feature1);
|
||||
features_ &= ~MakeFeatureMask(feature2);
|
||||
features_ &= ~MakeFeatureMask(feature3);
|
||||
}
|
||||
|
||||
CPUFeatures CPUFeatures::With(const CPUFeatures& other) const {
|
||||
CPUFeatures f(*this);
|
||||
f.Combine(other);
|
||||
return f;
|
||||
}
|
||||
|
||||
CPUFeatures CPUFeatures::With(Feature feature0,
|
||||
Feature feature1,
|
||||
Feature feature2,
|
||||
Feature feature3) const {
|
||||
CPUFeatures f(*this);
|
||||
f.Combine(feature0, feature1, feature2, feature3);
|
||||
return f;
|
||||
}
|
||||
|
||||
CPUFeatures CPUFeatures::Without(const CPUFeatures& other) const {
|
||||
CPUFeatures f(*this);
|
||||
f.Remove(other);
|
||||
return f;
|
||||
}
|
||||
|
||||
CPUFeatures CPUFeatures::Without(Feature feature0,
|
||||
Feature feature1,
|
||||
Feature feature2,
|
||||
Feature feature3) const {
|
||||
CPUFeatures f(*this);
|
||||
f.Remove(feature0, feature1, feature2, feature3);
|
||||
return f;
|
||||
}
|
||||
|
||||
bool CPUFeatures::Has(const CPUFeatures& other) const {
|
||||
return (features_ & other.features_) == other.features_;
|
||||
}
|
||||
|
||||
bool CPUFeatures::Has(Feature feature0,
|
||||
Feature feature1,
|
||||
Feature feature2,
|
||||
Feature feature3) const {
|
||||
uint64_t mask = MakeFeatureMask(feature0) | MakeFeatureMask(feature1) |
|
||||
MakeFeatureMask(feature2) | MakeFeatureMask(feature3);
|
||||
return (features_ & mask) == mask;
|
||||
}
|
||||
|
||||
size_t CPUFeatures::Count() const { return CountSetBits(features_); }
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, CPUFeatures::Feature feature) {
|
||||
// clang-format off
|
||||
switch (feature) {
|
||||
#define VIXL_FORMAT_FEATURE(SYMBOL, NAME, CPUINFO) \
|
||||
case CPUFeatures::SYMBOL: \
|
||||
return os << NAME;
|
||||
VIXL_CPU_FEATURE_LIST(VIXL_FORMAT_FEATURE)
|
||||
#undef VIXL_FORMAT_FEATURE
|
||||
case CPUFeatures::kNone:
|
||||
return os << "none";
|
||||
case CPUFeatures::kNumberOfFeatures:
|
||||
VIXL_UNREACHABLE();
|
||||
}
|
||||
// clang-format on
|
||||
VIXL_UNREACHABLE();
|
||||
return os;
|
||||
}
|
||||
|
||||
CPUFeatures::const_iterator CPUFeatures::begin() const {
|
||||
if (features_ == 0) return const_iterator(this, kNone);
|
||||
|
||||
int feature_number = CountTrailingZeros(features_);
|
||||
vixl::CPUFeatures::Feature feature =
|
||||
static_cast<CPUFeatures::Feature>(feature_number);
|
||||
return const_iterator(this, feature);
|
||||
}
|
||||
|
||||
CPUFeatures::const_iterator CPUFeatures::end() const {
|
||||
return const_iterator(this, kNone);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const CPUFeatures& features) {
|
||||
CPUFeatures::const_iterator it = features.begin();
|
||||
while (it != features.end()) {
|
||||
os << *it;
|
||||
++it;
|
||||
if (it != features.end()) os << ", ";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
bool CPUFeaturesConstIterator::operator==(
|
||||
const CPUFeaturesConstIterator& other) const {
|
||||
VIXL_ASSERT(IsValid());
|
||||
return (cpu_features_ == other.cpu_features_) && (feature_ == other.feature_);
|
||||
}
|
||||
|
||||
CPUFeatures::Feature CPUFeaturesConstIterator::operator++() { // Prefix
|
||||
VIXL_ASSERT(IsValid());
|
||||
do {
|
||||
// Find the next feature. The order is unspecified.
|
||||
feature_ = static_cast<CPUFeatures::Feature>(feature_ + 1);
|
||||
if (feature_ == CPUFeatures::kNumberOfFeatures) {
|
||||
feature_ = CPUFeatures::kNone;
|
||||
VIXL_STATIC_ASSERT(CPUFeatures::kNone == -1);
|
||||
}
|
||||
VIXL_ASSERT(CPUFeatures::kNone <= feature_);
|
||||
VIXL_ASSERT(feature_ < CPUFeatures::kNumberOfFeatures);
|
||||
// cpu_features_->Has(kNone) is always true, so this will terminate even if
|
||||
// the features list is empty.
|
||||
} while (!cpu_features_->Has(feature_));
|
||||
return feature_;
|
||||
}
|
||||
|
||||
CPUFeatures::Feature CPUFeaturesConstIterator::operator++(int) { // Postfix
|
||||
CPUFeatures::Feature result = feature_;
|
||||
++(*this);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace vixl
|
||||
@@ -0,0 +1,555 @@
|
||||
// Copyright 2015, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "utils-vixl.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
// The default NaN values (for FPCR.DN=1).
|
||||
const double kFP64DefaultNaN = RawbitsToDouble(UINT64_C(0x7ff8000000000000));
|
||||
const float kFP32DefaultNaN = RawbitsToFloat(0x7fc00000);
|
||||
const Float16 kFP16DefaultNaN = RawbitsToFloat16(0x7e00);
|
||||
|
||||
// Floating-point zero values.
|
||||
const Float16 kFP16PositiveZero = RawbitsToFloat16(0x0);
|
||||
const Float16 kFP16NegativeZero = RawbitsToFloat16(0x8000);
|
||||
|
||||
// Floating-point infinity values.
|
||||
const Float16 kFP16PositiveInfinity = RawbitsToFloat16(0x7c00);
|
||||
const Float16 kFP16NegativeInfinity = RawbitsToFloat16(0xfc00);
|
||||
const float kFP32PositiveInfinity = RawbitsToFloat(0x7f800000);
|
||||
const float kFP32NegativeInfinity = RawbitsToFloat(0xff800000);
|
||||
const double kFP64PositiveInfinity =
|
||||
RawbitsToDouble(UINT64_C(0x7ff0000000000000));
|
||||
const double kFP64NegativeInfinity =
|
||||
RawbitsToDouble(UINT64_C(0xfff0000000000000));
|
||||
|
||||
bool IsZero(Float16 value) {
|
||||
uint16_t bits = Float16ToRawbits(value);
|
||||
return (bits == Float16ToRawbits(kFP16PositiveZero) ||
|
||||
bits == Float16ToRawbits(kFP16NegativeZero));
|
||||
}
|
||||
|
||||
uint16_t Float16ToRawbits(Float16 value) { return value.rawbits_; }
|
||||
|
||||
uint32_t FloatToRawbits(float value) {
|
||||
uint32_t bits = 0;
|
||||
memcpy(&bits, &value, 4);
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
uint64_t DoubleToRawbits(double value) {
|
||||
uint64_t bits = 0;
|
||||
memcpy(&bits, &value, 8);
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
Float16 RawbitsToFloat16(uint16_t bits) {
|
||||
Float16 f;
|
||||
f.rawbits_ = bits;
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
float RawbitsToFloat(uint32_t bits) {
|
||||
float value = 0.0;
|
||||
memcpy(&value, &bits, 4);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
double RawbitsToDouble(uint64_t bits) {
|
||||
double value = 0.0;
|
||||
memcpy(&value, &bits, 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Float16Sign(internal::SimFloat16 val) {
|
||||
uint16_t rawbits = Float16ToRawbits(val);
|
||||
return ExtractUnsignedBitfield32(15, 15, rawbits);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Float16Exp(internal::SimFloat16 val) {
|
||||
uint16_t rawbits = Float16ToRawbits(val);
|
||||
return ExtractUnsignedBitfield32(14, 10, rawbits);
|
||||
}
|
||||
|
||||
uint32_t Float16Mantissa(internal::SimFloat16 val) {
|
||||
uint16_t rawbits = Float16ToRawbits(val);
|
||||
return ExtractUnsignedBitfield32(9, 0, rawbits);
|
||||
}
|
||||
|
||||
|
||||
uint32_t FloatSign(float val) {
|
||||
uint32_t rawbits = FloatToRawbits(val);
|
||||
return ExtractUnsignedBitfield32(31, 31, rawbits);
|
||||
}
|
||||
|
||||
|
||||
uint32_t FloatExp(float val) {
|
||||
uint32_t rawbits = FloatToRawbits(val);
|
||||
return ExtractUnsignedBitfield32(30, 23, rawbits);
|
||||
}
|
||||
|
||||
|
||||
uint32_t FloatMantissa(float val) {
|
||||
uint32_t rawbits = FloatToRawbits(val);
|
||||
return ExtractUnsignedBitfield32(22, 0, rawbits);
|
||||
}
|
||||
|
||||
|
||||
uint32_t DoubleSign(double val) {
|
||||
uint64_t rawbits = DoubleToRawbits(val);
|
||||
return static_cast<uint32_t>(ExtractUnsignedBitfield64(63, 63, rawbits));
|
||||
}
|
||||
|
||||
|
||||
uint32_t DoubleExp(double val) {
|
||||
uint64_t rawbits = DoubleToRawbits(val);
|
||||
return static_cast<uint32_t>(ExtractUnsignedBitfield64(62, 52, rawbits));
|
||||
}
|
||||
|
||||
|
||||
uint64_t DoubleMantissa(double val) {
|
||||
uint64_t rawbits = DoubleToRawbits(val);
|
||||
return ExtractUnsignedBitfield64(51, 0, rawbits);
|
||||
}
|
||||
|
||||
|
||||
internal::SimFloat16 Float16Pack(uint16_t sign,
|
||||
uint16_t exp,
|
||||
uint16_t mantissa) {
|
||||
uint16_t bits = (sign << 15) | (exp << 10) | mantissa;
|
||||
return RawbitsToFloat16(bits);
|
||||
}
|
||||
|
||||
|
||||
float FloatPack(uint32_t sign, uint32_t exp, uint32_t mantissa) {
|
||||
uint32_t bits = (sign << 31) | (exp << 23) | mantissa;
|
||||
return RawbitsToFloat(bits);
|
||||
}
|
||||
|
||||
|
||||
double DoublePack(uint64_t sign, uint64_t exp, uint64_t mantissa) {
|
||||
uint64_t bits = (sign << 63) | (exp << 52) | mantissa;
|
||||
return RawbitsToDouble(bits);
|
||||
}
|
||||
|
||||
|
||||
int Float16Classify(Float16 value) {
|
||||
uint16_t bits = Float16ToRawbits(value);
|
||||
uint16_t exponent_max = (1 << 5) - 1;
|
||||
uint16_t exponent_mask = exponent_max << 10;
|
||||
uint16_t mantissa_mask = (1 << 10) - 1;
|
||||
|
||||
uint16_t exponent = (bits & exponent_mask) >> 10;
|
||||
uint16_t mantissa = bits & mantissa_mask;
|
||||
if (exponent == 0) {
|
||||
if (mantissa == 0) {
|
||||
return FP_ZERO;
|
||||
}
|
||||
return FP_SUBNORMAL;
|
||||
} else if (exponent == exponent_max) {
|
||||
if (mantissa == 0) {
|
||||
return FP_INFINITE;
|
||||
}
|
||||
return FP_NAN;
|
||||
}
|
||||
return FP_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) {
|
||||
VIXL_ASSERT((reg_size % 8) == 0);
|
||||
int count = 0;
|
||||
for (unsigned i = 0; i < (reg_size / 16); i++) {
|
||||
if ((imm & 0xffff) == 0) {
|
||||
count++;
|
||||
}
|
||||
imm >>= 16;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int BitCount(uint64_t value) { return CountSetBits(value); }
|
||||
|
||||
// Float16 definitions.
|
||||
|
||||
Float16::Float16(double dvalue) {
|
||||
rawbits_ =
|
||||
Float16ToRawbits(FPToFloat16(dvalue, FPTieEven, kIgnoreDefaultNaN));
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
SimFloat16 SimFloat16::operator-() const {
|
||||
return RawbitsToFloat16(rawbits_ ^ 0x8000);
|
||||
}
|
||||
|
||||
// SimFloat16 definitions.
|
||||
SimFloat16 SimFloat16::operator+(SimFloat16 rhs) const {
|
||||
return static_cast<double>(*this) + static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
SimFloat16 SimFloat16::operator-(SimFloat16 rhs) const {
|
||||
return static_cast<double>(*this) - static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
SimFloat16 SimFloat16::operator*(SimFloat16 rhs) const {
|
||||
return static_cast<double>(*this) * static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
SimFloat16 SimFloat16::operator/(SimFloat16 rhs) const {
|
||||
return static_cast<double>(*this) / static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
bool SimFloat16::operator<(SimFloat16 rhs) const {
|
||||
return static_cast<double>(*this) < static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
bool SimFloat16::operator>(SimFloat16 rhs) const {
|
||||
return static_cast<double>(*this) > static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
bool SimFloat16::operator==(SimFloat16 rhs) const {
|
||||
if (IsNaN(*this) || IsNaN(rhs)) {
|
||||
return false;
|
||||
} else if (IsZero(rhs) && IsZero(*this)) {
|
||||
// +0 and -0 should be treated as equal.
|
||||
return true;
|
||||
}
|
||||
return this->rawbits_ == rhs.rawbits_;
|
||||
}
|
||||
|
||||
bool SimFloat16::operator!=(SimFloat16 rhs) const { return !(*this == rhs); }
|
||||
|
||||
bool SimFloat16::operator==(double rhs) const {
|
||||
return static_cast<double>(*this) == static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
SimFloat16::operator double() const {
|
||||
return FPToDouble(*this, kIgnoreDefaultNaN);
|
||||
}
|
||||
|
||||
Int64 BitCount(Uint32 value) { return CountSetBits(value.Get()); }
|
||||
|
||||
} // namespace internal
|
||||
|
||||
float FPToFloat(Float16 value, UseDefaultNaN DN, bool* exception) {
|
||||
uint16_t bits = Float16ToRawbits(value);
|
||||
uint32_t sign = bits >> 15;
|
||||
uint32_t exponent =
|
||||
ExtractUnsignedBitfield32(kFloat16MantissaBits + kFloat16ExponentBits - 1,
|
||||
kFloat16MantissaBits,
|
||||
bits);
|
||||
uint32_t mantissa =
|
||||
ExtractUnsignedBitfield32(kFloat16MantissaBits - 1, 0, bits);
|
||||
|
||||
switch (Float16Classify(value)) {
|
||||
case FP_ZERO:
|
||||
return (sign == 0) ? 0.0f : -0.0f;
|
||||
|
||||
case FP_INFINITE:
|
||||
return (sign == 0) ? kFP32PositiveInfinity : kFP32NegativeInfinity;
|
||||
|
||||
case FP_SUBNORMAL: {
|
||||
// Calculate shift required to put mantissa into the most-significant bits
|
||||
// of the destination mantissa.
|
||||
int shift = CountLeadingZeros(mantissa << (32 - 10));
|
||||
|
||||
// Shift mantissa and discard implicit '1'.
|
||||
mantissa <<= (kFloatMantissaBits - kFloat16MantissaBits) + shift + 1;
|
||||
mantissa &= (1 << kFloatMantissaBits) - 1;
|
||||
|
||||
// Adjust the exponent for the shift applied, and rebias.
|
||||
exponent = exponent - shift + (-15 + 127);
|
||||
break;
|
||||
}
|
||||
|
||||
case FP_NAN:
|
||||
if (IsSignallingNaN(value)) {
|
||||
if (exception != NULL) {
|
||||
*exception = true;
|
||||
}
|
||||
}
|
||||
if (DN == kUseDefaultNaN) return kFP32DefaultNaN;
|
||||
|
||||
// Convert NaNs as the processor would:
|
||||
// - The sign is propagated.
|
||||
// - The payload (mantissa) is transferred entirely, except that the top
|
||||
// bit is forced to '1', making the result a quiet NaN. The unused
|
||||
// (low-order) payload bits are set to 0.
|
||||
exponent = (1 << kFloatExponentBits) - 1;
|
||||
|
||||
// Increase bits in mantissa, making low-order bits 0.
|
||||
mantissa <<= (kFloatMantissaBits - kFloat16MantissaBits);
|
||||
mantissa |= 1 << 22; // Force a quiet NaN.
|
||||
break;
|
||||
|
||||
case FP_NORMAL:
|
||||
// Increase bits in mantissa, making low-order bits 0.
|
||||
mantissa <<= (kFloatMantissaBits - kFloat16MantissaBits);
|
||||
|
||||
// Change exponent bias.
|
||||
exponent += (-15 + 127);
|
||||
break;
|
||||
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
}
|
||||
return RawbitsToFloat((sign << 31) | (exponent << kFloatMantissaBits) |
|
||||
mantissa);
|
||||
}
|
||||
|
||||
|
||||
float FPToFloat(double value,
|
||||
FPRounding round_mode,
|
||||
UseDefaultNaN DN,
|
||||
bool* exception) {
|
||||
// Only the FPTieEven rounding mode is implemented.
|
||||
VIXL_ASSERT((round_mode == FPTieEven) || (round_mode == FPRoundOdd));
|
||||
USE(round_mode);
|
||||
|
||||
switch (std::fpclassify(value)) {
|
||||
case FP_NAN: {
|
||||
if (IsSignallingNaN(value)) {
|
||||
if (exception != NULL) {
|
||||
*exception = true;
|
||||
}
|
||||
}
|
||||
if (DN == kUseDefaultNaN) return kFP32DefaultNaN;
|
||||
|
||||
// Convert NaNs as the processor would:
|
||||
// - The sign is propagated.
|
||||
// - The payload (mantissa) is transferred as much as possible, except
|
||||
// that the top bit is forced to '1', making the result a quiet NaN.
|
||||
uint64_t raw = DoubleToRawbits(value);
|
||||
|
||||
uint32_t sign = raw >> 63;
|
||||
uint32_t exponent = (1 << 8) - 1;
|
||||
uint32_t payload =
|
||||
static_cast<uint32_t>(ExtractUnsignedBitfield64(50, 52 - 23, raw));
|
||||
payload |= (1 << 22); // Force a quiet NaN.
|
||||
|
||||
return RawbitsToFloat((sign << 31) | (exponent << 23) | payload);
|
||||
}
|
||||
|
||||
case FP_ZERO:
|
||||
case FP_INFINITE: {
|
||||
// In a C++ cast, any value representable in the target type will be
|
||||
// unchanged. This is always the case for +/-0.0 and infinities.
|
||||
return static_cast<float>(value);
|
||||
}
|
||||
|
||||
case FP_NORMAL:
|
||||
case FP_SUBNORMAL: {
|
||||
// Convert double-to-float as the processor would, assuming that FPCR.FZ
|
||||
// (flush-to-zero) is not set.
|
||||
uint64_t raw = DoubleToRawbits(value);
|
||||
// Extract the IEEE-754 double components.
|
||||
uint32_t sign = raw >> 63;
|
||||
// Extract the exponent and remove the IEEE-754 encoding bias.
|
||||
int32_t exponent =
|
||||
static_cast<int32_t>(ExtractUnsignedBitfield64(62, 52, raw)) - 1023;
|
||||
// Extract the mantissa and add the implicit '1' bit.
|
||||
uint64_t mantissa = ExtractUnsignedBitfield64(51, 0, raw);
|
||||
if (std::fpclassify(value) == FP_NORMAL) {
|
||||
mantissa |= (UINT64_C(1) << 52);
|
||||
}
|
||||
return FPRoundToFloat(sign, exponent, mantissa, round_mode);
|
||||
}
|
||||
}
|
||||
|
||||
VIXL_UNREACHABLE();
|
||||
return value;
|
||||
}
|
||||
|
||||
// TODO: We should consider implementing a full FPToDouble(Float16)
|
||||
// conversion function (for performance reasons).
|
||||
double FPToDouble(Float16 value, UseDefaultNaN DN, bool* exception) {
|
||||
// We can rely on implicit float to double conversion here.
|
||||
return FPToFloat(value, DN, exception);
|
||||
}
|
||||
|
||||
|
||||
double FPToDouble(float value, UseDefaultNaN DN, bool* exception) {
|
||||
switch (std::fpclassify(value)) {
|
||||
case FP_NAN: {
|
||||
if (IsSignallingNaN(value)) {
|
||||
if (exception != NULL) {
|
||||
*exception = true;
|
||||
}
|
||||
}
|
||||
if (DN == kUseDefaultNaN) return kFP64DefaultNaN;
|
||||
|
||||
// Convert NaNs as the processor would:
|
||||
// - The sign is propagated.
|
||||
// - The payload (mantissa) is transferred entirely, except that the top
|
||||
// bit is forced to '1', making the result a quiet NaN. The unused
|
||||
// (low-order) payload bits are set to 0.
|
||||
uint32_t raw = FloatToRawbits(value);
|
||||
|
||||
uint64_t sign = raw >> 31;
|
||||
uint64_t exponent = (1 << 11) - 1;
|
||||
uint64_t payload = ExtractUnsignedBitfield64(21, 0, raw);
|
||||
payload <<= (52 - 23); // The unused low-order bits should be 0.
|
||||
payload |= (UINT64_C(1) << 51); // Force a quiet NaN.
|
||||
|
||||
return RawbitsToDouble((sign << 63) | (exponent << 52) | payload);
|
||||
}
|
||||
|
||||
case FP_ZERO:
|
||||
case FP_NORMAL:
|
||||
case FP_SUBNORMAL:
|
||||
case FP_INFINITE: {
|
||||
// All other inputs are preserved in a standard cast, because every value
|
||||
// representable using an IEEE-754 float is also representable using an
|
||||
// IEEE-754 double.
|
||||
return static_cast<double>(value);
|
||||
}
|
||||
}
|
||||
|
||||
VIXL_UNREACHABLE();
|
||||
return static_cast<double>(value);
|
||||
}
|
||||
|
||||
|
||||
Float16 FPToFloat16(float value,
|
||||
FPRounding round_mode,
|
||||
UseDefaultNaN DN,
|
||||
bool* exception) {
|
||||
// Only the FPTieEven rounding mode is implemented.
|
||||
VIXL_ASSERT(round_mode == FPTieEven);
|
||||
USE(round_mode);
|
||||
|
||||
uint32_t raw = FloatToRawbits(value);
|
||||
int32_t sign = raw >> 31;
|
||||
int32_t exponent = ExtractUnsignedBitfield32(30, 23, raw) - 127;
|
||||
uint32_t mantissa = ExtractUnsignedBitfield32(22, 0, raw);
|
||||
|
||||
switch (std::fpclassify(value)) {
|
||||
case FP_NAN: {
|
||||
if (IsSignallingNaN(value)) {
|
||||
if (exception != NULL) {
|
||||
*exception = true;
|
||||
}
|
||||
}
|
||||
if (DN == kUseDefaultNaN) return kFP16DefaultNaN;
|
||||
|
||||
// Convert NaNs as the processor would:
|
||||
// - The sign is propagated.
|
||||
// - The payload (mantissa) is transferred as much as possible, except
|
||||
// that the top bit is forced to '1', making the result a quiet NaN.
|
||||
uint16_t result = (sign == 0) ? Float16ToRawbits(kFP16PositiveInfinity)
|
||||
: Float16ToRawbits(kFP16NegativeInfinity);
|
||||
result |= mantissa >> (kFloatMantissaBits - kFloat16MantissaBits);
|
||||
result |= (1 << 9); // Force a quiet NaN;
|
||||
return RawbitsToFloat16(result);
|
||||
}
|
||||
|
||||
case FP_ZERO:
|
||||
return (sign == 0) ? kFP16PositiveZero : kFP16NegativeZero;
|
||||
|
||||
case FP_INFINITE:
|
||||
return (sign == 0) ? kFP16PositiveInfinity : kFP16NegativeInfinity;
|
||||
|
||||
case FP_NORMAL:
|
||||
case FP_SUBNORMAL: {
|
||||
// Convert float-to-half as the processor would, assuming that FPCR.FZ
|
||||
// (flush-to-zero) is not set.
|
||||
|
||||
// Add the implicit '1' bit to the mantissa.
|
||||
mantissa += (1 << 23);
|
||||
return FPRoundToFloat16(sign, exponent, mantissa, round_mode);
|
||||
}
|
||||
}
|
||||
|
||||
VIXL_UNREACHABLE();
|
||||
return kFP16PositiveZero;
|
||||
}
|
||||
|
||||
|
||||
Float16 FPToFloat16(double value,
|
||||
FPRounding round_mode,
|
||||
UseDefaultNaN DN,
|
||||
bool* exception) {
|
||||
// Only the FPTieEven rounding mode is implemented.
|
||||
VIXL_ASSERT(round_mode == FPTieEven);
|
||||
USE(round_mode);
|
||||
|
||||
uint64_t raw = DoubleToRawbits(value);
|
||||
int32_t sign = raw >> 63;
|
||||
int64_t exponent = ExtractUnsignedBitfield64(62, 52, raw) - 1023;
|
||||
uint64_t mantissa = ExtractUnsignedBitfield64(51, 0, raw);
|
||||
|
||||
switch (std::fpclassify(value)) {
|
||||
case FP_NAN: {
|
||||
if (IsSignallingNaN(value)) {
|
||||
if (exception != NULL) {
|
||||
*exception = true;
|
||||
}
|
||||
}
|
||||
if (DN == kUseDefaultNaN) return kFP16DefaultNaN;
|
||||
|
||||
// Convert NaNs as the processor would:
|
||||
// - The sign is propagated.
|
||||
// - The payload (mantissa) is transferred as much as possible, except
|
||||
// that the top bit is forced to '1', making the result a quiet NaN.
|
||||
uint16_t result = (sign == 0) ? Float16ToRawbits(kFP16PositiveInfinity)
|
||||
: Float16ToRawbits(kFP16NegativeInfinity);
|
||||
result |= mantissa >> (kDoubleMantissaBits - kFloat16MantissaBits);
|
||||
result |= (1 << 9); // Force a quiet NaN;
|
||||
return RawbitsToFloat16(result);
|
||||
}
|
||||
|
||||
case FP_ZERO:
|
||||
return (sign == 0) ? kFP16PositiveZero : kFP16NegativeZero;
|
||||
|
||||
case FP_INFINITE:
|
||||
return (sign == 0) ? kFP16PositiveInfinity : kFP16NegativeInfinity;
|
||||
case FP_NORMAL:
|
||||
case FP_SUBNORMAL: {
|
||||
// Convert double-to-half as the processor would, assuming that FPCR.FZ
|
||||
// (flush-to-zero) is not set.
|
||||
|
||||
// Add the implicit '1' bit to the mantissa.
|
||||
mantissa += (UINT64_C(1) << 52);
|
||||
return FPRoundToFloat16(sign, exponent, mantissa, round_mode);
|
||||
}
|
||||
}
|
||||
|
||||
VIXL_UNREACHABLE();
|
||||
return kFP16PositiveZero;
|
||||
}
|
||||
|
||||
} // namespace vixl
|
||||
Reference in New Issue
Block a user