--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.asm.sparc/src/org/graalvm/compiler/asm/sparc/SPARCAssembler.java 2016-12-09 00:46:48.478224560 -0800 @@ -0,0 +1,2694 @@ +/* + * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.asm.sparc; + +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Icc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Always; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Add; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Addc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Addcc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.And; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Andcc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Andn; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Andncc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Casa; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Casxa; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Flushw; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Fpop1; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Fpop2; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Impdep1; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Jmpl; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Lddf; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Ldf; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Ldsb; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Ldsh; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Ldsw; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Ldub; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Lduh; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Lduw; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Lduwa; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Ldx; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Ldxa; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Membar; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Movcc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Mulx; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Or; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Popc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Prefetch; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Rd; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Restore; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Save; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Sdivx; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Sll; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Sllx; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Sra; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Srax; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Srl; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Srlx; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Stb; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Stdf; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Stf; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Sth; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Stw; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Stx; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Stxa; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Sub; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Subcc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Udivx; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Wr; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Xnor; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Xor; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Xorcc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fabsd; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fabss; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Faddd; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fadds; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fdivd; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fdivs; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fdtoi; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fdtos; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fdtox; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fitod; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fitos; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fmovd; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fmovs; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fmuld; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fmuls; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fnegd; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fnegs; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fpadd32; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fsmuld; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fsqrtd; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fsqrts; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fsrc2d; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fsrc2s; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fstod; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fstoi; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fstox; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fsubd; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fsubs; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fxtod; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fxtos; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fzerod; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fzeros; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Movdtox; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Movstosw; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Movwtos; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Movxtod; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.UMulxhi; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Ops.ArithOp; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Ops.LdstOp; +import static java.lang.String.format; +import static jdk.vm.ci.sparc.SPARC.CPU; +import static jdk.vm.ci.sparc.SPARC.FPUd; +import static jdk.vm.ci.sparc.SPARC.FPUs; +import static jdk.vm.ci.sparc.SPARC.g0; +import static jdk.vm.ci.sparc.SPARC.g2; +import static jdk.vm.ci.sparc.SPARC.g5; +import static jdk.vm.ci.sparc.SPARC.g7; +import static jdk.vm.ci.sparc.SPARC.o7; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.graalvm.compiler.asm.Assembler; +import org.graalvm.compiler.asm.Label; +import org.graalvm.compiler.asm.NumUtil; +import org.graalvm.compiler.common.PermanentBailoutException; +import org.graalvm.compiler.debug.GraalError; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.PlatformKind; +import jdk.vm.ci.sparc.SPARC; +import jdk.vm.ci.sparc.SPARC.CPUFeature; +import jdk.vm.ci.sparc.SPARCKind; + +/** + * This class implements an assembler that can encode most SPARC instructions. + */ +public abstract class SPARCAssembler extends Assembler { + + /** + * Constructs an assembler for the SPARC architecture. + */ + public SPARCAssembler(TargetDescription target) { + super(target); + } + + /** + * Size of an SPARC assembler instruction in Bytes. + */ + public static final int INSTRUCTION_SIZE = 4; + + /** + * Size in bytes which are cleared by stxa %g0, [%rd] ASI_ST_BLKINIT_PRIMARY. + */ + public static final int BLOCK_ZERO_LENGTH = 64; + + public static final int CCR_ICC_SHIFT = 0; + public static final int CCR_XCC_SHIFT = 4; + public static final int CCR_V_SHIFT = 1; + + public static final int MEMBAR_LOAD_LOAD = 1; + public static final int MEMBAR_STORE_LOAD = 2; + public static final int MEMBAR_LOAD_STORE = 3; + public static final int MEMBAR_STORE_STORE = 4; + + private static final Ops[] OPS; + private static final Op2s[] OP2S; + private static final Op3s[][] OP3S; + + private ArrayList delaySlotOptimizationPoints = new ArrayList<>(5); + + static { + Ops[] ops = Ops.values(); + OPS = new Ops[ops.length]; + for (Ops op : ops) { + OPS[op.value] = op; + } + Op2s[] op2s = Op2s.values(); + OP2S = new Op2s[op2s.length]; + for (Op2s op2 : op2s) { + OP2S[op2.value] = op2; + } + OP3S = new Op3s[2][64]; + for (Op3s op3 : Op3s.values()) { + if (op3.value >= 1 << 6) { + throw new RuntimeException("Error " + op3 + " " + op3.value); + } + OP3S[op3.op.value & 1][op3.value] = op3; + } + } + + public enum Ops { + // @formatter:off + BranchOp(0b00), + CallOp(0b01), + ArithOp(0b10), + LdstOp(0b11); + // @formatter:on + + private final int value; + + Ops(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public boolean appliesTo(int instructionWord) { + int opShift = 30; + return (instructionWord >>> opShift) == value; + } + } + + public enum Op2s { + // Checkstyle: stop + // @formatter:off + Illtrap(0b000), + Bpr (0b011), + Fb (0b110), + Fbp (0b101), + Br (0b010), + Bp (0b001), + Cb (0b111), + Sethi (0b100); + // @formatter:on + // Checkstyle: resume + + private final int value; + + Op2s(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Op2s byValue(int value) { + return OP2S[value]; + } + } + + private static final int COMMUTATIVE = 1; + private static final int BINARY = 2; + private static final int UNARY = 4; + private static final int VOID_IN = 8; + + public enum Op3s { + // Checkstyle: stop + // @formatter:off + Add(0x00, "add", ArithOp, BINARY | COMMUTATIVE), + And(0x01, "and", ArithOp, BINARY | COMMUTATIVE), + Or(0x02, "or", ArithOp, BINARY | COMMUTATIVE), + Xor(0x03, "xor", ArithOp, BINARY | COMMUTATIVE), + Sub(0x04, "sub", ArithOp, BINARY), + Andn(0x05, "andn", ArithOp, BINARY | COMMUTATIVE), + Orn(0x06, "orn", ArithOp, BINARY | COMMUTATIVE), + Xnor(0x07, "xnor", ArithOp, BINARY | COMMUTATIVE), + Addc(0x08, "addc", ArithOp, BINARY | COMMUTATIVE), + Mulx(0x09, "mulx", ArithOp, BINARY | COMMUTATIVE), + Umul(0x0A, "umul", ArithOp, BINARY | COMMUTATIVE), + Smul(0x0B, "smul", ArithOp, BINARY | COMMUTATIVE), + Subc(0x0C, "subc", ArithOp, BINARY), + Udivx(0x0D, "udivx", ArithOp, BINARY), + Udiv(0x0E, "udiv", ArithOp, BINARY), + Sdiv(0x0F, "sdiv", ArithOp, BINARY), + + Addcc(0x10, "addcc", ArithOp, BINARY | COMMUTATIVE), + Andcc(0x11, "andcc", ArithOp, BINARY | COMMUTATIVE), + Orcc(0x12, "orcc", ArithOp, BINARY | COMMUTATIVE), + Xorcc(0x13, "xorcc", ArithOp, BINARY | COMMUTATIVE), + Subcc(0x14, "subcc", ArithOp, BINARY), + Andncc(0x15, "andncc", ArithOp, BINARY | COMMUTATIVE), + Orncc(0x16, "orncc", ArithOp, BINARY | COMMUTATIVE), + Xnorcc(0x17, "xnorcc", ArithOp, BINARY | COMMUTATIVE), + Addccc(0x18, "addccc", ArithOp, BINARY | COMMUTATIVE), + + Umulcc(0x1A, "umulcc", ArithOp, BINARY | COMMUTATIVE), + Smulcc(0x1B, "smulcc", ArithOp, BINARY | COMMUTATIVE), + Subccc(0x1C, "subccc", ArithOp, BINARY), + Udivcc(0x1E, "udivcc", ArithOp, BINARY), + Sdivcc(0x1F, "sdivcc", ArithOp, BINARY), + + Mulscc(0x24, "mulscc", ArithOp, BINARY | COMMUTATIVE), + Sll(0x25, "sll", ArithOp, BINARY), + Sllx(0x25, "sllx", ArithOp, BINARY), + Srl(0x26, "srl", ArithOp, BINARY), + Srlx(0x26, "srlx", ArithOp, BINARY), + Sra(0x27, "srax", ArithOp, BINARY), + Srax(0x27, "srax", ArithOp, BINARY), + Membar(0x28, "membar", ArithOp), + + Flushw(0x2B, "flushw", ArithOp), + Movcc(0x2C, "movcc", ArithOp), + Sdivx(0x2D, "sdivx", ArithOp, BINARY), + Popc(0x2E, "popc", ArithOp, UNARY), + Movr(0x2F, "movr", ArithOp, BINARY), + + Fpop1(0b11_0100, "fpop1", ArithOp), + Fpop2(0b11_0101, "fpop2", ArithOp), + Impdep1(0b11_0110, "impdep1", ArithOp), + Impdep2(0b11_0111, "impdep2", ArithOp), + Jmpl(0x38, "jmpl", ArithOp), + Rett(0x39, "rett", ArithOp), + Trap(0x3a, "trap", ArithOp), + Flush(0x3b, "flush", ArithOp), + Save(0x3c, "save", ArithOp), + Restore(0x3d, "restore", ArithOp), + Retry(0x3e, "retry", ArithOp), + + + Casa(0b111100, "casa", LdstOp), + Casxa(0b111110, "casxa", LdstOp), + Prefetch(0b101101, "prefetch", LdstOp), + Prefetcha(0b111101, "prefetcha", LdstOp), + + Lduw (0b00_0000, "lduw", LdstOp), + Ldub (0b00_0001, "ldub", LdstOp), + Lduh (0b00_0010, "lduh", LdstOp), + Stw (0b00_0100, "stw", LdstOp), + Stb (0b00_0101, "stb", LdstOp), + Sth (0b00_0110, "sth", LdstOp), + Ldsw (0b00_1000, "ldsw", LdstOp), + Ldsb (0b00_1001, "ldsb", LdstOp), + Ldsh (0b00_1010, "ldsh", LdstOp), + Ldx (0b00_1011, "ldx", LdstOp), + Stx (0b00_1110, "stx", LdstOp), + + Ldf (0b10_0000, "ldf", LdstOp), + Ldfsr (0b10_0001, "ldfsr", LdstOp), + Ldaf (0b10_0010, "ldaf", LdstOp), + Lddf (0b10_0011, "lddf", LdstOp), + Stf (0b10_0100, "stf", LdstOp), + Stfsr (0b10_0101, "stfsr", LdstOp), + Staf (0b10_0110, "staf", LdstOp), + Stdf (0b10_0111, "stdf", LdstOp), + + Stba (0b01_0101, "stba", LdstOp), + Stha (0b01_0110, "stha", LdstOp), + Stwa (0b01_0100, "stwa", LdstOp), + Stxa (0b01_1110, "stxa", LdstOp), + + Ldsba (0b01_1001, "ldsba", LdstOp), + Ldsha (0b01_1010, "ldsha", LdstOp), + Ldswa (0b01_1000, "ldswa", LdstOp), + Lduba (0b01_0001, "lduba", LdstOp), + Lduha (0b01_0010, "lduha", LdstOp), + Lduwa (0b01_0000, "lduwa", LdstOp), + + Ldxa (0b01_1011, "ldxa", LdstOp), + + Rd (0b10_1000, "rd", ArithOp), + Wr (0b11_0000, "wr", ArithOp), + + Tcc(0b11_1010, "tcc", ArithOp); + + // @formatter:on + // Checkstyle: resume + + private final int value; + private final String operator; + private final Ops op; + private final int flags; + + Op3s(int value, String name, Ops op) { + this(value, name, op, 0); + } + + Op3s(int value, String name, Ops op, int flags) { + this.value = value; + this.operator = name; + this.op = op; + this.flags = flags; + } + + public int getValue() { + return value; + } + + public String getOperator() { + return operator; + } + + public boolean throwsException() { + if (op == LdstOp) { + return true; + } + switch (this) { + case Udiv: + case Udivx: + case Sdiv: + case Sdivx: + case Udivcc: + case Sdivcc: + return true; + default: + return false; + } + } + + public boolean isBinary() { + return (flags & BINARY) != 0; + } + + public boolean isUnary() { + return (flags & UNARY) != 0; + } + + public boolean isCommutative() { + return (flags & COMMUTATIVE) != 0; + } + } + + public enum Opfs { + // @formatter:off + + Fmovs(0b0_0000_0001, "fmovs", Fpop1, UNARY), + Fmovd(0b0_0000_0010, "fmovd", Fpop1, UNARY), + Fmovq(0b0_0000_0011, "fmovq", Fpop1, UNARY), + Fnegs(0x05, "fnegs", Fpop1, UNARY), + Fnegd(0x06, "fnegd", Fpop1, UNARY), + Fnegq(0x07, "fnegq", Fpop1, UNARY), + Fabss(0x09, "fabss", Fpop1, UNARY), + Fabsd(0x0A, "fabsd", Fpop1, UNARY), + Fabsq(0x0B, "fabsq", Fpop1, UNARY), + + // start VIS1 + Fpadd32(0x52, "fpadd32", Impdep1, BINARY | COMMUTATIVE), + Fzerod(0x60, "fzerod", Impdep1, VOID_IN), + Fzeros(0x61, "fzeros", Impdep1, VOID_IN), + Fsrc2d(0x78, "fsrc2d", Impdep1, UNARY), + Fsrc2s(0x79, "fsrc2s", Impdep1, UNARY), + // end VIS1 + + // start VIS3 + Movdtox(0x110, "movdtox", Impdep1, UNARY), + Movstouw(0x111, "movstouw", Impdep1, UNARY), + Movstosw(0x113, "movstosw", Impdep1, UNARY), + Movxtod(0x118, "movxtod", Impdep1, UNARY), + Movwtos(0b1_0001_1001, "movwtos", Impdep1, UNARY), + UMulxhi(0b0_0001_0110, "umulxhi", Impdep1, BINARY | COMMUTATIVE), + // end VIS3 + + Fadds(0x41, "fadds", Fpop1, BINARY | COMMUTATIVE), + Faddd(0x42, "faddd", Fpop1, BINARY | COMMUTATIVE), + Fsubs(0x45, "fsubs", Fpop1, BINARY), + Fsubd(0x46, "fsubd", Fpop1, BINARY), + Fmuls(0x49, "fmuls", Fpop1, BINARY | COMMUTATIVE), + Fmuld(0x4A, "fmuld", Fpop1, BINARY | COMMUTATIVE), + Fdivs(0x4D, "fdivs", Fpop1, BINARY), + Fdivd(0x4E, "fdivd", Fpop1, BINARY), + + Fsqrts(0x29, "fsqrts", Fpop1, UNARY), + Fsqrtd(0x2A, "fsqrtd", Fpop1, UNARY), + + Fsmuld(0x69, "fsmuld", Fpop1, BINARY | COMMUTATIVE), + + Fstoi(0xD1, "fstoi", Fpop1, UNARY), + Fdtoi(0xD2, "fdtoi", Fpop1, UNARY), + Fstox(0x81, "fstox", Fpop1, UNARY), + Fdtox(0x82, "fdtox", Fpop1, UNARY), + Fxtos(0x84, "fxtos", Fpop1, UNARY), + Fxtod(0x88, "fxtod", Fpop1, UNARY), + Fitos(0xC4, "fitos", Fpop1, UNARY), + Fdtos(0xC6, "fdtos", Fpop1, UNARY), + Fitod(0xC8, "fitod", Fpop1, UNARY), + Fstod(0xC9, "fstod", Fpop1, UNARY), + + + Fcmps(0x51, "fcmps", Fpop2, BINARY), + Fcmpd(0x52, "fcmpd", Fpop2, BINARY); + + // @formatter:on + + private final int value; + private final String operator; + private final Op3s op3; + private final int flags; + + Opfs(int value, String op, Op3s op3, int flags) { + this.value = value; + this.operator = op; + this.op3 = op3; + this.flags = flags; + } + + public int getValue() { + return value; + } + + public String getOperator() { + return operator; + } + + public boolean isBinary() { + return (flags & BINARY) != 0; + } + + public boolean isUnary() { + return (flags & UNARY) != 0; + } + + public boolean isCommutative() { + return (flags & COMMUTATIVE) != 0; + } + } + + public enum OpfLow { + Fmovscc(0b00_0001, "fmovscc", Fpop2), + Fmovdcc(0b00_0010, "fmovdcc", Fpop2); + + private final int value; + private final String operator; + private final Op3s op3; + + OpfLow(int value, String op, Op3s op3) { + this.value = value; + this.operator = op; + this.op3 = op3; + } + + @Override + public String toString() { + return operator; + } + } + + public enum Annul { + ANNUL(1), + NOT_ANNUL(0); + public final int flag; + + Annul(int flag) { + this.flag = flag; + } + } + + public enum BranchPredict { + PREDICT_TAKEN(1), + PREDICT_NOT_TAKEN(0); + public final int flag; + + BranchPredict(int flag) { + this.flag = flag; + } + } + + public enum MembarMask { + // @formatter:off + + StoreStore(1 << 3, "storestore"), + LoadStore(1 << 2, "loadstore"), + StoreLoad(1 << 1, "storeload"), + LoadLoad(1 << 0, "loadload"), + Sync(1 << 6, "sync"), + MemIssue(1 << 5, "memissue"), + LookAside(1 << 4, "lookaside"); + + // @formatter:on + + private final int value; + private final String operator; + + MembarMask(int value, String op) { + this.value = value; + this.operator = op; + } + + public int getValue() { + return value | 0x2000; + } + + public String getOperator() { + return operator; + } + } + + /** + * Condition Codes to use for instruction. + */ + public enum CC { + // @formatter:off + /** + * Condition is considered as 32bit operation condition. + */ + Icc(0b00, "icc", false), + /** + * Condition is considered as 64bit operation condition. + */ + Xcc(0b10, "xcc", false), + Fcc0(0b00, "fcc0", true), + Fcc1(0b01, "fcc1", true), + Fcc2(0b10, "fcc2", true), + Fcc3(0b11, "fcc3", true); + + // @formatter:on + + private final int value; + private final String operator; + private boolean isFloat; + + CC(int value, String op, boolean isFloat) { + this.value = value; + this.operator = op; + this.isFloat = isFloat; + } + + public int getValue() { + return value; + } + + public String getOperator() { + return operator; + } + + public static CC forKind(PlatformKind kind) { + if (kind.equals(SPARCKind.XWORD)) { + return Xcc; + } else if (kind.equals(SPARCKind.WORD)) { + return Icc; + } else if (kind.equals(SPARCKind.SINGLE) || kind.equals(SPARCKind.DOUBLE)) { + return Fcc0; + } else { + throw new IllegalArgumentException("Unknown kind: " + kind); + } + } + } + + public enum ConditionFlag { + // @formatter:off + + // for FBfcc & FBPfcc instruction + F_Never(0, "f_never"), + F_NotEqual(1, "f_notEqual"), + F_LessOrGreater(2, "f_lessOrGreater"), + F_UnorderedOrLess(3, "f_unorderedOrLess"), + F_Less(4, "f_less"), + F_UnorderedOrGreater(5, "f_unorderedOrGreater"), + F_Greater(6, "f_greater"), + F_Unordered(7, "f_unordered"), + F_Always(8, "f_always"), + F_Equal(9, "f_equal"), + F_UnorderedOrEqual(10, "f_unorderedOrEqual"), + F_GreaterOrEqual(11, "f_greaterOrEqual"), + F_UnorderedGreaterOrEqual(12, "f_unorderedGreaterOrEqual"), + F_LessOrEqual(13, "f_lessOrEqual"), + F_UnorderedOrLessOrEqual(14, "f_unorderedOrLessOrEqual"), + F_Ordered(15, "f_ordered"), + + // for integers + Never(0, "never"), + Equal(1, "equal", true), + Zero(1, "zero"), + LessEqual(2, "lessEqual", true), + Less(3, "less", true), + LessEqualUnsigned(4, "lessEqualUnsigned", true), + LessUnsigned(5, "lessUnsigned", true), + CarrySet(5, "carrySet"), + Negative(6, "negative", true), + OverflowSet(7, "overflowSet", true), + Always(8, "always"), + NotEqual(9, "notEqual", true), + NotZero(9, "notZero"), + Greater(10, "greater", true), + GreaterEqual(11, "greaterEqual", true), + GreaterUnsigned(12, "greaterUnsigned", true), + GreaterEqualUnsigned(13, "greaterEqualUnsigned", true), + CarryClear(13, "carryClear"), + Positive(14, "positive", true), + OverflowClear(15, "overflowClear", true); + + // @formatter:on + + private final int value; + private final String operator; + private boolean forCBcond = false; + + ConditionFlag(int value, String op) { + this(value, op, false); + } + + ConditionFlag(int value, String op, boolean cbcond) { + this.value = value; + this.operator = op; + this.forCBcond = cbcond; + } + + public boolean isCBCond() { + return forCBcond; + } + + public int getValue() { + return value; + } + + public String getOperator() { + return operator; + } + + public ConditionFlag negate() { + //@formatter:off + switch (this) { + case F_Never : return F_Always; + case F_Always : return F_Never; + case F_NotEqual : return F_Equal; + case F_Equal : return F_NotEqual; + case F_LessOrGreater : return F_UnorderedOrEqual; + case F_UnorderedOrEqual : return F_LessOrGreater; + case F_Less : return F_UnorderedGreaterOrEqual; + case F_UnorderedGreaterOrEqual: return F_Less; + case F_LessOrEqual : return F_UnorderedOrGreater; + case F_UnorderedOrGreater : return F_LessOrEqual; + case F_Greater : return F_UnorderedOrLessOrEqual; + case F_UnorderedOrLessOrEqual : return F_Greater; + case F_GreaterOrEqual : return F_UnorderedOrLess; + case F_UnorderedOrLess : return F_GreaterOrEqual; + case F_Unordered : return F_Ordered; + case F_Ordered : return F_Unordered; + case Never : return Always; + case Always : return Never; + case Equal : return NotEqual; + case NotEqual : return Equal; + case Zero : return NotZero; + case NotZero : return Zero; + case LessEqual : return Greater; + case Greater : return LessEqual; + case Less : return GreaterEqual; + case GreaterEqual : return Less; + case LessEqualUnsigned : return GreaterUnsigned; + case GreaterUnsigned : return LessEqualUnsigned; + case LessUnsigned : return GreaterEqualUnsigned; + case GreaterEqualUnsigned : return LessUnsigned; + case CarrySet : return CarryClear; + case CarryClear : return CarrySet; + case Negative : return Positive; + case Positive : return Negative; + case OverflowSet : return OverflowClear; + case OverflowClear : return OverflowSet; + default: + throw new InternalError(); + } + //@formatter:on + } + + public ConditionFlag mirror() { + switch (this) { + //@formatter:off + case F_Less : return F_Greater; + case F_Greater : return F_Less; + case F_LessOrEqual : return F_GreaterOrEqual; + case F_UnorderedGreaterOrEqual: return F_UnorderedOrLessOrEqual; + case F_UnorderedOrGreater : return F_UnorderedOrLess; + case F_UnorderedOrLessOrEqual : return F_UnorderedGreaterOrEqual; + case F_GreaterOrEqual : return F_LessOrEqual; + case F_UnorderedOrLess : return F_UnorderedOrGreater; + case LessEqual : return GreaterEqual; + case Greater : return Less; + case Less : return Greater; + case GreaterEqual : return LessEqual; + case LessEqualUnsigned : return GreaterEqualUnsigned; + case GreaterUnsigned : return LessUnsigned; + case LessUnsigned : return GreaterUnsigned; + case GreaterEqualUnsigned : return LessEqualUnsigned; + default: + return this; + //@formatter:on + } + } + + } + + public enum RCondition { + // @formatter:off + + Rc_z(0b001, "rc_z"), + Rc_lez(0b010, "rc_lez"), + Rc_lz(0b011, "rc_lz"), + Rc_nz(0b101, "rc_nz"), + Rc_gz(0b110, "rc_gz"), + Rc_gez(0b111, "rc_gez"), + Rc_last(Rc_gez.getValue(), "rc_last"); + + // @formatter:on + + private final int value; + private final String operator; + + RCondition(int value, String op) { + this.value = value; + this.operator = op; + } + + public int getValue() { + return value; + } + + public String getOperator() { + return operator; + } + } + + /** + * Represents the Address Space Identifier defined in the SPARC architecture. + */ + public enum Asi { + // @formatter:off + + INVALID(-1), + ASI_PRIMARY(0x80), + ASI_PRIMARY_NOFAULT(0x82), + ASI_PRIMARY_LITTLE(0x88), + // Block initializing store + ASI_ST_BLKINIT_PRIMARY(0xE2), + // Most-Recently-Used (MRU) BIS variant + ASI_ST_BLKINIT_MRU_PRIMARY(0xF2); + + // @formatter:on + + private final int value; + + Asi(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public boolean isValid() { + return value != INVALID.getValue(); + } + } + + public enum Fcn { + SeveralWritesAndPossiblyReads(2), + SeveralReadsWeak(0), + OneRead(1), + OneWrite(3), + Page(4), + NearestUnifiedCache(17), + SeveralReadsStrong(20), + OneReadStrong(21), + SeveralWritesAndPossiblyReadsStrong(22), + OneWriteStrong(23); + + private final int value; + + Fcn(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + /** + * Specifies various bit fields used in SPARC instructions. + */ + @SuppressWarnings("unused") + public abstract static class BitSpec { + private static final BitSpec op = new ContinousBitSpec(31, 30, "op"); + private static final BitSpec op2 = new ContinousBitSpec(24, 22, "op2"); + private static final BitSpec op3 = new ContinousBitSpec(24, 19, "op3"); + private static final BitSpec opf = new ContinousBitSpec(13, 5, "opf"); + private static final BitSpec opfLow = new ContinousBitSpec(10, 5, "opfLow"); + private static final BitSpec opfCC = new ContinousBitSpec(13, 11, "opfCC"); + private static final BitSpec opfCond = new ContinousBitSpec(17, 14, "opfCond"); + private static final BitSpec rd = new ContinousBitSpec(29, 25, "rd"); + private static final BitSpec rs1 = new ContinousBitSpec(18, 14, "rs1"); + private static final BitSpec rs2 = new ContinousBitSpec(4, 0, "rs2"); + private static final BitSpec simm13 = new ContinousBitSpec(12, 0, true, "simm13"); + private static final BitSpec shcnt32 = new ContinousBitSpec(4, 0, "shcnt32"); + private static final BitSpec shcnt64 = new ContinousBitSpec(5, 0, "shcnt64"); + private static final BitSpec imm22 = new ContinousBitSpec(21, 0, "imm22"); + private static final BitSpec immAsi = new ContinousBitSpec(12, 5, "immASI"); + private static final BitSpec i = new ContinousBitSpec(13, 13, "i"); + private static final BitSpec disp19 = new ContinousBitSpec(18, 0, true, "disp19"); + private static final BitSpec disp22 = new ContinousBitSpec(21, 0, true, "disp22"); + private static final BitSpec disp30 = new ContinousBitSpec(29, 0, true, "disp30"); + private static final BitSpec a = new ContinousBitSpec(29, 29, "a"); + private static final BitSpec p = new ContinousBitSpec(19, 19, "p"); + private static final BitSpec x = new ContinousBitSpec(12, 12, "x"); + private static final BitSpec cond = new ContinousBitSpec(28, 25, "cond"); + private static final BitSpec rcond = new ContinousBitSpec(27, 25, "rcond"); + private static final BitSpec cc = new ContinousBitSpec(21, 20, "cc"); + private static final BitSpec fcc = new ContinousBitSpec(26, 25, "cc"); + private static final BitSpec d16lo = new ContinousBitSpec(13, 0, "d16lo"); + private static final BitSpec d16hi = new ContinousBitSpec(21, 20, true, "d16hi"); + private static final BitSpec d16 = new CompositeBitSpec(d16hi, d16lo); + // Movcc + private static final BitSpec movccLo = new ContinousBitSpec(12, 11, "cc_lo"); + private static final BitSpec movccHi = new ContinousBitSpec(18, 18, "cc_hi"); + private static final BitSpec movccCond = new ContinousBitSpec(17, 14, "cond"); + private static final BitSpec simm11 = new ContinousBitSpec(10, 0, true, "simm11"); + + // CBCond + private static final BitSpec cLo = new ContinousBitSpec(27, 25, "cLo"); + private static final BitSpec cHi = new ContinousBitSpec(29, 29, "cHi"); + private static final BitSpec c = new CompositeBitSpec(cHi, cLo); + private static final BitSpec cbcond = new ContinousBitSpec(28, 28, "cbcond"); + private static final BitSpec cc2 = new ContinousBitSpec(21, 21, "cc2"); + private static final BitSpec d10Lo = new ContinousBitSpec(12, 5, "d10Lo"); + private static final BitSpec d10Hi = new ContinousBitSpec(20, 19, true, "d10Hi"); + private static final BitSpec d10 = new CompositeBitSpec(d10Hi, d10Lo); + private static final BitSpec simm5 = new ContinousBitSpec(4, 0, true, "simm5"); + + protected final boolean signExtend; + + public BitSpec(boolean signExtend) { + super(); + this.signExtend = signExtend; + } + + public final boolean isSignExtend() { + return signExtend; + } + + public abstract int setBits(int word, int value); + + public abstract int getBits(int word); + + public abstract int getWidth(); + + public abstract boolean valueFits(int value); + } + + public static final class ContinousBitSpec extends BitSpec { + private final int hiBit; + private final int lowBit; + private final int width; + private final int mask; + private final String name; + + public ContinousBitSpec(int hiBit, int lowBit, String name) { + this(hiBit, lowBit, false, name); + } + + public ContinousBitSpec(int hiBit, int lowBit, boolean signExt, String name) { + super(signExt); + this.hiBit = hiBit; + this.lowBit = lowBit; + this.width = hiBit - lowBit + 1; + mask = ((1 << width) - 1) << lowBit; + this.name = name; + } + + @Override + public int setBits(int word, int value) { + assert valueFits(value) : String.format("Value 0x%x for field %s does not fit.", value, this); + return (word & ~mask) | ((value << lowBit) & mask); + } + + @Override + public int getBits(int word) { + if (signExtend) { + return ((word & mask) << (31 - hiBit)) >> (32 - width); + } else { + return (word & mask) >>> lowBit; + } + } + + @Override + public int getWidth() { + return width; + } + + @Override + public String toString() { + return String.format("%s [%d:%d]", name, hiBit, lowBit); + } + + @Override + public boolean valueFits(int value) { + if (signExtend) { + return isSimm(value, getWidth()); + } else { + return isImm(value, getWidth()); + } + } + } + + public static final class CompositeBitSpec extends BitSpec { + private final BitSpec left; + private final int leftWidth; + private final BitSpec right; + private final int rightWidth; + private final int width; + + public CompositeBitSpec(BitSpec left, BitSpec right) { + super(left.isSignExtend()); + assert !right.isSignExtend() : String.format("Right field %s must not be sign extended", right); + this.left = left; + this.leftWidth = left.getWidth(); + this.right = right; + this.rightWidth = right.getWidth(); + this.width = leftWidth + rightWidth; + } + + @Override + public int getBits(int word) { + int l = left.getBits(word); + int r = right.getBits(word); + return (l << rightWidth) | r; + } + + @Override + public int setBits(int word, int value) { + int l = leftBits(value); + int r = rightBits(value); + return left.setBits(right.setBits(word, r), l); + } + + private int leftBits(int value) { + return getBits(value, width - 1, rightWidth, signExtend); + } + + private int rightBits(int value) { + return getBits(value, rightWidth - 1, 0, false); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public String toString() { + return String.format("CompositeBitSpec[%s, %s]", left, right); + } + + @Override + public boolean valueFits(int value) { + int l = leftBits(value); + int r = rightBits(value); + return left.valueFits(l) && right.valueFits(r); + } + + private static int getBits(int inst, int hiBit, int lowBit, boolean signExtended) { + int shifted = inst >> lowBit; + if (signExtended) { + return shifted; + } else { + return shifted & ((1 << (hiBit - lowBit + 1)) - 1); + } + } + } + + public static class BitKey { + private final BitSpec spec; + private final int value; + + public BitKey(BitSpec spec, int value) { + super(); + this.spec = spec; + this.value = value; + } + + @Override + public String toString() { + return String.format("BitKey %s=%s", spec, value); + } + } + + /** + * Represents a prefix tree of {@link BitSpec} objects to find the most accurate SPARCOp. + */ + public static final class BitKeyIndex { + private final BitSpec spec; + private final Map nodes; + private SPARCOp op; + + public BitKeyIndex(SPARCOp op) { + assert op != null; + this.op = op; + this.nodes = null; + this.spec = null; + } + + public BitKeyIndex(BitSpec spec) { + assert spec != null; + this.op = null; + this.nodes = new HashMap<>(4); + this.spec = spec; + } + + /** + * Adds operation to the index. + * + * @param keys Ordered by the importance + * @param operation Operation represented by this list of keys + */ + private void addOp(List keys, SPARCOp operation) { + assert keys.size() > 0; + BitKey[] firstKeys = keys.get(0); + for (BitKey first : firstKeys) { + assert first.spec.equals(spec) : first.spec + " " + spec; + BitKeyIndex node; + if (keys.size() == 1) { + if (nodes.containsKey(first.value)) { + node = nodes.get(first.value); + assert node.op == null : node + " " + keys; + node.op = operation; + } else { + assert !nodes.containsKey(first.value) : "Index must be unique. Existing key: " + nodes.get(first.value); + node = new BitKeyIndex(operation); + } + } else { + node = nodes.get(first.value); + BitKey[] next = keys.get(1); + if (node == null) { + for (int i = 1; i < next.length; i++) { + assert next[i - 1].spec.equals(next[i].spec) : "All spec on this node must equal"; + } + node = new BitKeyIndex(next[0].spec); + } + node.addOp(keys.subList(1, keys.size()), operation); + } + nodes.put(first.value, node); + } + } + + /** + * Finds the best matching {@link SPARCOp} for this instruction. + */ + public SPARCOp find(int inst) { + if (nodes != null) { + int key = spec.getBits(inst); + BitKeyIndex sub = nodes.get(key); + if (sub == null) { + if (op != null) { + return op; + } else { + throw new RuntimeException(String.format("%s 0x%x, 0x%x %s", spec, inst, key, nodes)); + } + } + return sub.find(inst); + } else { + return this.op; + } + } + + @Override + public String toString() { + return this.op == null ? this.spec + ": " + this.nodes : this.op.toString(); + } + } + + public static final Bpcc BPCC = new Bpcc(Op2s.Bp); + public static final Bpcc FBPCC = new Bpcc(Op2s.Fbp); + public static final CBCond CBCOND = new CBCond(); + public static final Bpr BPR = new Bpr(); + public static final Br BR = new Br(); + public static final Sethi SETHI = new Sethi(); + public static final FMOVcc FMOVSCC = new FMOVcc(OpfLow.Fmovscc); + public static final FMOVcc FMOVDCC = new FMOVcc(OpfLow.Fmovdcc); + public static final MOVicc MOVicc = new MOVicc(); + public static final OpfOp OPF = new OpfOp(); + public static final Op3Op OP3 = new Op3Op(); + public static final SPARCOp LDST = new SPARCOp(Ops.LdstOp); + public static final SPARCOp BRANCH = new SPARCOp(Ops.BranchOp); + public static final SPARCOp CALL = new SPARCOp(Ops.CallOp); + private static final BitKeyIndex INDEX = new BitKeyIndex(BitSpec.op); + + static { + for (SPARCOp op : SPARCOp.OPS) { + INDEX.addOp(op.getKeys(), op); + } + } + + public static SPARCOp getSPARCOp(int inst) { + return INDEX.find(inst); + } + + /** + * Represents a class of SPARC instruction and gives methods to modify its fields. + */ + public static class SPARCOp { + private final Ops op; + private final BitKey opKey; + private List keyFields; + private static final List OPS = new ArrayList<>(); + + public SPARCOp(Ops op) { + super(); + this.op = op; + this.opKey = new BitKey(BitSpec.op, op.value); + OPS.add(this); + } + + protected int setBits(int word) { + return BitSpec.op.setBits(word, op.value); + } + + public boolean match(int inst) { + for (BitKey[] keys : keyFields) { + for (BitKey k : keys) { + if (k.spec.getBits(inst) != k.value) { + return false; + } + } + } + return true; + } + + protected List getKeys() { + if (keyFields == null) { + keyFields = new ArrayList<>(4); + keyFields.add(new BitKey[]{opKey}); + } + return keyFields; + } + + public Ops getOp(int inst) { + return SPARCAssembler.OPS[BitSpec.op.getBits(inst)]; + } + + @Override + public String toString() { + String name = getClass().getName(); + name = name.substring(name.lastIndexOf(".") + 1); + return name + "[op: " + op + "]"; + } + } + + /** + * Base class for control transfer operations; provides access to the disp field. + */ + public abstract static class ControlTransferOp extends SPARCOp { + private final Op2s op2; + private final boolean delaySlot; + private final BitSpec disp; + private final BitKey[] op2Key; + + private ControlTransferOp(Ops op, Op2s op2, boolean delaySlot, BitSpec disp) { + super(op); + this.op2 = op2; + this.delaySlot = delaySlot; + this.disp = disp; + this.op2Key = new BitKey[]{new BitKey(BitSpec.op2, op2.value)}; + } + + public boolean hasDelaySlot() { + return delaySlot; + } + + @Override + protected int setBits(int word) { + return BitSpec.op2.setBits(super.setBits(word), op2.value); + } + + protected int setDisp(int inst, SPARCMacroAssembler masm, Label lab) { + if (lab.isBound()) { + int d = (lab.position() - masm.position()) / 4; + return setDisp(inst, d); + } else { + masm.patchUnbound(lab); + return inst; + } + } + + public int setDisp(int inst, int d) { + assert this.match(inst); + if (!isValidDisp(d)) { + throw new PermanentBailoutException("Too large displacement 0x%x in field %s in instruction %s", d, this.disp, this); + } + return this.disp.setBits(inst, d); + } + + public boolean isValidDisp(int d) { + return this.disp.valueFits(d); + } + + public int setAnnul(int inst, boolean a) { + return BitSpec.a.setBits(inst, a ? 1 : 0); + } + + @Override + protected List getKeys() { + List keys = super.getKeys(); + keys.add(op2Key); + return keys; + } + + public int getDisp(int inst) { + return this.disp.getBits(inst); + } + + public abstract boolean isAnnulable(int inst); + + public abstract boolean isConditional(int inst); + } + + public static final class Bpcc extends ControlTransferOp { + public Bpcc(Op2s op2) { + super(Ops.BranchOp, op2, true, BitSpec.disp19); + } + + public void emit(SPARCMacroAssembler masm, CC cc, ConditionFlag cf, Annul annul, BranchPredict p, Label lab) { + int inst = setBits(0); + inst = BitSpec.a.setBits(inst, annul.flag); + inst = BitSpec.cond.setBits(inst, cf.value); + inst = BitSpec.cc.setBits(inst, cc.value); + inst = BitSpec.p.setBits(inst, p.flag); + masm.insertNopAfterCBCond(); + masm.emitInt(setDisp(inst, masm, lab)); + } + + @Override + public boolean isAnnulable(int inst) { + return isConditional(inst); + } + + @Override + public boolean isConditional(int inst) { + int cond = BitSpec.cond.getBits(inst); + return cond != ConditionFlag.Always.value && cond != ConditionFlag.Never.value; + } + } + + public static final class Br extends ControlTransferOp { + public Br() { + super(Ops.BranchOp, Op2s.Br, true, BitSpec.disp22); + } + + @Override + public boolean isAnnulable(int inst) { + return isConditional(inst); + } + + @Override + public boolean isConditional(int inst) { + int cond = BitSpec.cond.getBits(inst); + return cond != ConditionFlag.Always.value && cond != ConditionFlag.Never.value; + } + + public void emit(SPARCMacroAssembler masm, ConditionFlag cond, Annul a, Label lab) { + int inst = setBits(0); + inst = BitSpec.cond.setBits(inst, cond.value); + inst = BitSpec.a.setBits(inst, a.flag); + masm.insertNopAfterCBCond(); + masm.emitInt(setDisp(inst, masm, lab)); + } + } + + public static final class Bpr extends ControlTransferOp { + private static final BitKey CBCOND_KEY = new BitKey(BitSpec.cbcond, 0); + + public Bpr() { + super(Ops.BranchOp, Op2s.Bpr, true, BitSpec.d16); + } + + public void emit(SPARCMacroAssembler masm, RCondition rcond, Annul a, BranchPredict p, Register rs1, Label lab) { + int inst = setBits(0); + inst = BitSpec.rcond.setBits(inst, rcond.value); + inst = BitSpec.a.setBits(inst, a.flag); + inst = BitSpec.p.setBits(inst, p.flag); + inst = BitSpec.rs1.setBits(inst, rs1.encoding); + masm.insertNopAfterCBCond(); + masm.emitInt(setDisp(inst, masm, lab)); + } + + @Override + protected List getKeys() { + List keys = super.getKeys(); + keys.add(new BitKey[]{CBCOND_KEY}); + return keys; + } + + @Override + public boolean isAnnulable(int inst) { + return isConditional(inst); + } + + @Override + public boolean isConditional(int inst) { + int cond = BitSpec.cond.getBits(inst); + return cond != ConditionFlag.Always.value && cond != ConditionFlag.Never.value; + } + } + + public static final class CBCond extends ControlTransferOp { + private static final BitKey CBCOND_KEY = new BitKey(BitSpec.cbcond, 1); + + private CBCond() { + super(Ops.BranchOp, Op2s.Bpr, false, BitSpec.d10); + } + + @Override + protected List getKeys() { + List keys = super.getKeys(); + keys.add(new BitKey[]{CBCOND_KEY}); + return keys; + } + + public void emit(SPARCMacroAssembler masm, ConditionFlag cf, boolean cc2, Register rs1, Register rs2, Label lab) { + int inst = setBits(0, cf, cc2, rs1); + inst = BitSpec.rs2.setBits(inst, rs2.encoding); + inst = BitSpec.i.setBits(inst, 0); + masm.insertNopAfterCBCond(); + emit(masm, lab, inst); + } + + public void emit(SPARCMacroAssembler masm, ConditionFlag cf, boolean cc2, Register rs1, int simm5, Label lab) { + int inst = setBits(0, cf, cc2, rs1); + inst = BitSpec.simm5.setBits(inst, simm5); + inst = BitSpec.i.setBits(inst, 1); + emit(masm, lab, inst); + } + + private void emit(SPARCMacroAssembler masm, Label lab, int baseInst) { + int inst = baseInst; + masm.insertNopAfterCBCond(); + masm.emitInt(setDisp(inst, masm, lab)); + } + + private int setBits(int base, ConditionFlag cf, boolean cc2, Register rs1) { + int inst = super.setBits(base); + inst = BitSpec.rs1.setBits(inst, rs1.encoding); + inst = BitSpec.cc2.setBits(inst, cc2 ? 1 : 0); + inst = BitSpec.c.setBits(inst, cf.value); + return BitSpec.cbcond.setBits(inst, 1); + } + + @Override + public boolean isAnnulable(int inst) { + return false; + } + + @Override + public boolean isConditional(int inst) { + return true; + } + } + + public static class Op2Op extends SPARCOp { + private final Op2s op2; + private final BitKey op2Key; + + public Op2Op(Ops op, Op2s op2) { + super(op); + this.op2 = op2; + op2Key = new BitKey(BitSpec.op2, op2.value); + } + + @Override + protected int setBits(int word) { + int result = super.setBits(word); + return BitSpec.op2.setBits(result, op2.value); + } + + @Override + protected List getKeys() { + List keys = super.getKeys(); + keys.add(new BitKey[]{op2Key}); + return keys; + } + } + + public static final class Sethi extends Op2Op { + public Sethi() { + super(Ops.BranchOp, Op2s.Sethi); + } + + public static Register getRS1(int word) { + int regNum = BitSpec.rs1.getBits(word); + return SPARC.cpuRegisters.get(regNum); + } + + public static int getImm22(int word) { + return BitSpec.imm22.getBits(word); + } + + public static boolean isNop(int inst) { + return getRS1(inst).equals(g0) && getImm22(inst) == 0; + } + } + + public static final class Op3Op extends SPARCOp { + public Op3Op() { + super(ArithOp); + } + + public Op3s getOp3(int inst) { + assert match(inst); + return OP3S[ArithOp.value & 1][BitSpec.op3.getBits(inst)]; + } + + public static void emit(SPARCMacroAssembler masm, Op3s opcode, Register rs1, Register rs2, Register rd) { + int instruction = setBits(0, opcode, rs1, rd); + instruction = BitSpec.rs2.setBits(instruction, rs2.encoding); + instruction = BitSpec.i.setBits(instruction, 0); + masm.emitInt(instruction); + } + + public static void emit(SPARCMacroAssembler masm, Op3s opcode, Register rs1, int simm13, Register rd) { + int instruction = setBits(0, opcode, rs1, rd); + instruction = BitSpec.i.setBits(instruction, 1); + BitSpec immediateSpec; + switch (opcode) { + case Sllx: + case Srlx: + case Srax: + immediateSpec = BitSpec.shcnt64; + break; + case Sll: + case Srl: + case Sra: + immediateSpec = BitSpec.shcnt32; + break; + default: + immediateSpec = BitSpec.simm13; + break; + } + instruction = immediateSpec.setBits(instruction, simm13); + masm.emitInt(instruction); + } + + private static int setBits(int instruction, Op3s op3, Register rs1, Register rd) { + assert op3.op.equals(ArithOp); + int tmp = BitSpec.op3.setBits(instruction, op3.value); + switch (op3) { + case Sllx: + case Srlx: + case Srax: + tmp = BitSpec.x.setBits(tmp, 1); + break; + } + tmp = BitSpec.op.setBits(tmp, op3.op.value); + tmp = BitSpec.rd.setBits(tmp, rd.encoding); + return BitSpec.rs1.setBits(tmp, rs1.encoding); + } + } + + /** + * Used for interfacing FP and GP conditional move instructions. + */ + public interface CMOV { + void emit(SPARCMacroAssembler masm, ConditionFlag condition, CC cc, Register rs2, Register rd); + + void emit(SPARCMacroAssembler masm, ConditionFlag condition, CC cc, int simm11, Register rd); + } + + public static final class MOVicc extends SPARCOp implements CMOV { + private static final Op3s op3 = Movcc; + + public MOVicc() { + super(ArithOp); + } + + @Override + public void emit(SPARCMacroAssembler masm, ConditionFlag condition, CC cc, Register rs2, Register rd) { + int inst = setBits(0, condition, cc, rd); + inst = BitSpec.rs2.setBits(inst, rs2.encoding()); + masm.emitInt(inst); + } + + @Override + public void emit(SPARCMacroAssembler masm, ConditionFlag condition, CC cc, int simm11, Register rd) { + int inst = setBits(0, condition, cc, rd); + inst = BitSpec.i.setBits(inst, 1); + inst = BitSpec.simm11.setBits(inst, simm11); + masm.emitInt(inst); + } + + protected int setBits(int word, ConditionFlag condition, CC cc, Register rd) { + int inst = super.setBits(word); + inst = BitSpec.rd.setBits(inst, rd.encoding()); + inst = BitSpec.op3.setBits(inst, op3.value); + inst = BitSpec.movccCond.setBits(inst, condition.value); + inst = BitSpec.movccLo.setBits(inst, cc.value); + return BitSpec.movccHi.setBits(inst, cc.isFloat ? 0 : 1); + } + + @Override + protected List getKeys() { + List keys = super.getKeys(); + keys.add(new BitKey[]{new BitKey(BitSpec.op3, op3.value)}); + return keys; + } + } + + public static final class FMOVcc extends SPARCOp implements CMOV { + private OpfLow opfLow; + + public FMOVcc(OpfLow opfLow) { + super(ArithOp); + this.opfLow = opfLow; + } + + @Override + public void emit(SPARCMacroAssembler masm, ConditionFlag condition, CC cc, Register rs2, Register rd) { + int inst = setBits(0); + inst = BitSpec.rd.setBits(inst, rd.encoding()); + inst = BitSpec.op3.setBits(inst, opfLow.op3.value); + inst = BitSpec.opfCond.setBits(inst, condition.value); + inst = BitSpec.opfCC.setBits(inst, cc.value); + inst = BitSpec.opfLow.setBits(inst, opfLow.value); + inst = BitSpec.rs2.setBits(inst, rs2.encoding()); + masm.emitInt(inst); + } + + @Override + public void emit(SPARCMacroAssembler masm, ConditionFlag condition, CC cc, int simm11, Register rd) { + throw new IllegalArgumentException("FMOVCC cannot be used with immediate value"); + } + + @Override + protected List getKeys() { + List keys = super.getKeys(); + keys.add(new BitKey[]{new BitKey(BitSpec.op3, opfLow.op3.value)}); + keys.add(new BitKey[]{new BitKey(BitSpec.opfLow, opfLow.value)}); + return keys; + } + } + + public static final class OpfOp extends SPARCOp { + + private BitKey[] op3Keys; + + public OpfOp(BitKey... op3Keys) { + super(ArithOp); + this.op3Keys = op3Keys; + } + + public OpfOp() { + // @formatter:off + this(new BitKey[]{ + new BitKey(BitSpec.op3, Op3s.Fpop1.value), + new BitKey(BitSpec.op3, Op3s.Fpop2.value), + new BitKey(BitSpec.op3, Op3s.Impdep1.value), + new BitKey(BitSpec.op3, Op3s.Impdep2.value)}); + // @formatter:on + } + + public static void emit(SPARCMacroAssembler masm, Opfs opf, Register rs1, Register rs2, Register rd) { + int instruction = setBits(0, opf, rs1, rs2); + instruction = BitSpec.rd.setBits(instruction, rd.encoding); + instruction = BitSpec.i.setBits(instruction, 0); + masm.emitInt(instruction); + } + + public static void emitFcmp(SPARCMacroAssembler masm, Opfs opf, CC cc, Register rs1, Register rs2) { + assert opf.equals(Opfs.Fcmpd) || opf.equals(Opfs.Fcmps) : opf; + int instruction = setBits(0, opf, rs1, rs2); + instruction = BitSpec.fcc.setBits(instruction, cc.value); + masm.emitInt(instruction); + } + + private static int setBits(int instruction, Opfs opf, Register rs1, Register rs2) { + int tmp = BitSpec.op.setBits(instruction, opf.op3.op.value); + tmp = BitSpec.op3.setBits(tmp, opf.op3.value); + tmp = BitSpec.opf.setBits(tmp, opf.value); + tmp = BitSpec.rs1.setBits(tmp, rs1.encoding); + return BitSpec.rs2.setBits(tmp, rs2.encoding); + } + + @Override + protected List getKeys() { + List keys = super.getKeys(); + keys.add(op3Keys); + // @formatter:on + return keys; + } + } + + public static boolean isCPURegister(Register... regs) { + for (Register reg : regs) { + if (!isCPURegister(reg)) { + return false; + } + } + return true; + } + + public static boolean isCPURegister(Register r) { + return r.getRegisterCategory().equals(CPU); + } + + public static boolean isGlobalRegister(Register r) { + return isCPURegister(r) && g0.number <= r.number && r.number <= g7.number; + } + + public static boolean isSingleFloatRegister(Register r) { + return r.getRegisterCategory().equals(FPUs); + } + + public static boolean isDoubleFloatRegister(Register r) { + return r.getRegisterCategory().equals(FPUd); + } + + public boolean hasFeature(CPUFeature feature) { + return ((SPARC) this.target.arch).features.contains(feature); + } + + public static final int simm(int x, int nbits) { + // assert_signed_range(x, nbits); + return x & ((1 << nbits) - 1); + } + + public static final boolean isImm(int x, int nbits) { + // assert_signed_range(x, nbits); + return simm(x, nbits) == x; + } + + /** + * Minimum value for signed immediate ranges. + */ + public static long minSimm(long nbits) { + return -(1L << (nbits - 1)); + } + + /** + * Maximum value for signed immediate ranges. + */ + public static long maxSimm(long nbits) { + return (1L << (nbits - 1)) - 1; + } + + /** + * Test if imm is within signed immediate range for nbits. + */ + public static boolean isSimm(long imm, int nbits) { + return minSimm(nbits) <= imm && imm <= maxSimm(nbits); + } + + public static boolean isSimm10(long imm) { + return isSimm(imm, 10); + } + + public static boolean isSimm11(long imm) { + return isSimm(imm, 11); + } + + public static boolean isSimm11(JavaConstant constant) { + return constant.isNull() || isSimm11(constant.asLong()); + } + + public static boolean isSimm5(JavaConstant constant) { + return constant.isNull() || isSimm(constant.asLong(), 5); + } + + public static boolean isSimm13(int imm) { + return isSimm(imm, 13); + } + + public static boolean isSimm13(JavaConstant constant) { + long bits; + switch (constant.getJavaKind()) { + case Double: + bits = Double.doubleToRawLongBits(constant.asDouble()); + break; + case Float: + bits = Float.floatToRawIntBits(constant.asFloat()); + break; + case Object: + return constant.isNull(); + default: + bits = constant.asLong(); + break; + } + return constant.isNull() || isSimm13(bits); + } + + public static boolean isSimm13(long imm) { + return NumUtil.isInt(imm) && isSimm(imm, 13); + } + + public static boolean isWordDisp30(long imm) { + return isSimm(imm, 30 + 2); + } + + public static final int hi22(int x) { + return x >>> 10; + } + + public static final int lo10(int x) { + return x & ((1 << 10) - 1); + } + + // @formatter:off + /** + * Instruction format for Fmt00 instructions. This abstraction is needed as it + * makes the patching easier later on. + *
+     * | 00  |    a   | op2 |               b                         |
+     * |31 30|29    25|24 22|21                                      0|
+     * 
+ */ + // @formatter:on + protected void fmt00(int a, int op2, int b) { + assert isImm(a, 5) && isImm(op2, 3) && isImm(b, 22) : String.format("a: 0x%x op2: 0x%x b: 0x%x", a, op2, b); + int word = 0; + BitSpec.op.setBits(word, 0); + BitSpec.rd.setBits(word, a); + BitSpec.op2.setBits(word, op2); + BitSpec.imm22.setBits(word, b); + emitInt(a << 25 | op2 << 22 | b); + } + + private void op3(Op3s op3, Opfs opf, Register rs1, Register rs2, Register rd) { + int b = opf.value << 5 | (rs2 == null ? 0 : rs2.encoding); + fmt(op3.op.value, rd.encoding, op3.value, rs1 == null ? 0 : rs1.encoding, b); + } + + protected void op3(Op3s op3, Register rs1, Register rs2, Register rd) { + int b = rs2 == null ? 0 : rs2.encoding; + int xBit = getXBit(op3); + fmt(op3.op.value, rd.encoding, op3.value, rs1 == null ? 0 : rs1.encoding, b | xBit); + } + + protected void op3(Op3s op3, Register rs1, int simm13, Register rd) { + assert isSimm13(simm13) : simm13; + int i = 1 << 13; + int simm13WithX = simm13 | getXBit(op3); + fmt(op3.op.value, rd.encoding, op3.value, rs1.encoding, i | simm13WithX & ((1 << 13) - 1)); + } + + public void insertNopAfterCBCond() { + int pos = position() - INSTRUCTION_SIZE; + if (pos == 0) { + return; + } + int inst = getInt(pos); + if (CBCOND.match(inst)) { + nop(); + } + } + + protected int patchUnbound(Label label) { + label.addPatchAt(position()); + return 0; + } + + // @formatter:off + /** + * NOP. + *
+     * | 00  |00000| 100 |                0                    |
+     * |31 30|29 25|24 22|21                                  0|
+     * 
+ */ + // @formatter:on + public void nop() { + emitInt(1 << 24); + } + + public void sethi(int imm22, Register dst) { + fmt00(dst.encoding, Op2s.Sethi.value, imm22); + } + + // @formatter:off + /** + * Instruction format for calls. + *
+     * | 01  |                      disp30                             |
+     * |31 30|29                                                      0|
+     * 
+ * + * @return Position of the call instruction + */ + // @formatter:on + public int call(int disp30) { + assert isImm(disp30, 30); + insertNopAfterCBCond(); + int before = position(); + int instr = 1 << 30; + instr |= disp30; + emitInt(instr); + return before; + } + + public void add(Register rs1, Register rs2, Register rd) { + op3(Add, rs1, rs2, rd); + } + + public void add(Register rs1, int simm13, Register rd) { + op3(Add, rs1, simm13, rd); + } + + public void addc(Register rs1, Register rs2, Register rd) { + op3(Addc, rs1, rs2, rd); + } + + public void addc(Register rs1, int simm13, Register rd) { + op3(Addc, rs1, simm13, rd); + } + + public void addcc(Register rs1, Register rs2, Register rd) { + op3(Addcc, rs1, rs2, rd); + } + + public void addcc(Register rs1, int simm13, Register rd) { + op3(Addcc, rs1, simm13, rd); + } + + public void and(Register rs1, Register rs2, Register rd) { + op3(And, rs1, rs2, rd); + } + + public void and(Register rs1, int simm13, Register rd) { + op3(And, rs1, simm13, rd); + } + + public void andcc(Register rs1, Register rs2, Register rd) { + op3(Andcc, rs1, rs2, rd); + } + + public void andcc(Register rs1, int simm13, Register rd) { + op3(Andcc, rs1, simm13, rd); + } + + public void andn(Register rs1, Register rs2, Register rd) { + op3(Andn, rs1, rs2, rd); + } + + public void andn(Register rs1, int simm13, Register rd) { + op3(Andn, rs1, simm13, rd); + } + + public void andncc(Register rs1, Register rs2, Register rd) { + op3(Andncc, rs1, rs2, rd); + } + + public void andncc(Register rs1, int simm13, Register rd) { + op3(Andncc, rs1, simm13, rd); + } + + public void movwtos(Register rs2, Register rd) { + assert isSingleFloatRegister(rd) && isCPURegister(rs2) : String.format("%s %s", rs2, rd); + op3(Impdep1, Movwtos, null, rs2, rd); + } + + public void umulxhi(Register rs1, Register rs2, Register rd) { + op3(Impdep1, UMulxhi, rs1, rs2, rd); + } + + public void fdtos(Register rs2, Register rd) { + assert isSingleFloatRegister(rd) && isDoubleFloatRegister(rs2) : String.format("%s %s", rs2, rd); + op3(Fpop1, Fdtos, null, rs2, rd); + } + + public void movstouw(Register rs2, Register rd) { + assert isSingleFloatRegister(rs2) && isCPURegister(rd) : String.format("%s %s", rs2, rd); + op3(Impdep1, Movstosw, null, rs2, rd); + } + + public void movstosw(Register rs2, Register rd) { + assert isSingleFloatRegister(rs2) && isCPURegister(rd) : String.format("%s %s", rs2, rd); + op3(Impdep1, Movstosw, null, rs2, rd); + } + + public void movdtox(Register rs2, Register rd) { + assert isDoubleFloatRegister(rs2) && isCPURegister(rd) : String.format("%s %s", rs2, rd); + op3(Impdep1, Movdtox, null, rs2, rd); + } + + public void movxtod(Register rs2, Register rd) { + assert isCPURegister(rs2) && isDoubleFloatRegister(rd) : String.format("%s %s", rs2, rd); + op3(Impdep1, Movxtod, null, rs2, rd); + } + + public void fadds(Register rs1, Register rs2, Register rd) { + op3(Fpop1, Fadds, rs1, rs2, rd); + } + + public void faddd(Register rs1, Register rs2, Register rd) { + op3(Fpop1, Faddd, rs1, rs2, rd); + } + + public void fdivs(Register rs1, Register rs2, Register rd) { + op3(Fpop1, Fdivs, rs1, rs2, rd); + } + + public void fdivd(Register rs1, Register rs2, Register rd) { + op3(Fpop1, Fdivd, rs1, rs2, rd); + } + + public void fmovs(Register rs2, Register rd) { + op3(Fpop1, Fmovs, null, rs2, rd); + } + + public void fmovd(Register rs2, Register rd) { + op3(Fpop1, Fmovd, null, rs2, rd); + } + + public void fsrc2s(Register rs2, Register rd) { + op3(Impdep1, Fsrc2s, null, rs2, rd); + } + + public void fsrc2d(Register rs2, Register rd) { + op3(Impdep1, Fsrc2d, null, rs2, rd); + } + + public void fmuls(Register rs1, Register rs2, Register rd) { + op3(Fpop1, Fmuls, rs1, rs2, rd); + } + + public void fsmuld(Register rs1, Register rs2, Register rd) { + op3(Fpop1, Fsmuld, rs1, rs2, rd); + } + + public void fmuld(Register rs1, Register rs2, Register rd) { + op3(Fpop1, Fmuld, rs1, rs2, rd); + } + + public void fnegs(Register rs2, Register rd) { + op3(Fpop1, Fnegs, null, rs2, rd); + } + + public void fnegd(Register rs2, Register rd) { + op3(Fpop1, Fnegd, null, rs2, rd); + } + + /** + * Helper method to determine if the instruction needs the X bit set. + */ + private static int getXBit(Op3s op3) { + switch (op3) { + case Sllx: + case Srax: + case Srlx: + return 1 << 12; + default: + return 0; + } + } + + public void fstoi(Register rs2, Register rd) { + op3(Fpop1, Fstoi, null, rs2, rd); + } + + public void fstox(Register rs2, Register rd) { + op3(Fpop1, Fstox, null, rs2, rd); + } + + public void fdtox(Register rs2, Register rd) { + op3(Fpop1, Fdtox, null, rs2, rd); + } + + public void fstod(Register rs2, Register rd) { + op3(Fpop1, Fstod, null, rs2, rd); + } + + public void fdtoi(Register rs2, Register rd) { + op3(Fpop1, Fdtoi, null, rs2, rd); + } + + public void fitos(Register rs2, Register rd) { + op3(Fpop1, Fitos, null, rs2, rd); + } + + public void fitod(Register rs2, Register rd) { + op3(Fpop1, Fitod, null, rs2, rd); + } + + public void fxtos(Register rs2, Register rd) { + op3(Fpop1, Fxtos, null, rs2, rd); + } + + public void fxtod(Register rs2, Register rd) { + op3(Fpop1, Fxtod, null, rs2, rd); + } + + public void fzeros(Register rd) { + op3(Impdep1, Fzeros, null, null, rd); + } + + public void fzerod(Register rd) { + op3(Impdep1, Fzerod, null, null, rd); + } + + public void flushw() { + op3(Flushw, g0, g0, g0); + } + + public void fsqrtd(Register rs2, Register rd) { + op3(Fpop1, Fsqrtd, null, rs2, rd); + } + + public void fsqrts(Register rs2, Register rd) { + op3(Fpop1, Fsqrts, null, rs2, rd); + } + + public void fabss(Register rs2, Register rd) { + op3(Fpop1, Fabss, null, rs2, rd); + } + + public void fabsd(Register rs2, Register rd) { + op3(Fpop1, Fabsd, null, rs2, rd); + } + + public void fsubs(Register rs1, Register rs2, Register rd) { + op3(Fpop1, Fsubs, rs1, rs2, rd); + } + + public void fsubd(Register rs1, Register rs2, Register rd) { + op3(Fpop1, Fsubd, rs1, rs2, rd); + } + + // @formatter:off + /** + * Instruction format for fcmp. + *
+     * | 10  | --- |cc1|cc0|desc |   rs1   |   opf  | rs2 |
+     * |31 30|29 27|26 |25 |24 19|18     14|13     5|4   0|
+     * 
+ */ + // @formatter:on + public void fcmp(CC cc, Opfs opf, Register rs1, Register rs2) { + int a = cc.value; + int b = opf.value << 5 | rs2.encoding; + delaySlotOptimizationPoints.add(position()); + fmt10(a, Fpop2.value, rs1.encoding, b); + } + + // @formatter:off + /** + * Instruction format for most arithmetic stuff. + *
+     * |  10 | rd  | op3 | rs1 |   b   |
+     * |31 30|29 25|24 19|18 14|13    0|
+     * 
+ */ + // @formatter:on + protected void fmt10(int rd, int op3, int rs1, int b) { + fmt(0b10, rd, op3, rs1, b); + } + + // @formatter:off + /** + * Instruction format for most arithmetic stuff. + *
+     * |  op | rd  | op3 | rs1 |   b   |
+     * |31 30|29 25|24 19|18 14|13    0|
+     * 
+ */ + // @formatter:on + protected void fmt(int op, int rd, int op3, int rs1, int b) { + assert isImm(rd, 5) && isImm(op3, 6) && isImm(b, 14) : String.format("rd: 0x%x op3: 0x%x b: 0x%x", rd, op3, b); + int instr = op << 30 | rd << 25 | op3 << 19 | rs1 << 14 | b; + emitInt(instr); + } + + public void illtrap(int const22) { + fmt00(0, Op2s.Illtrap.value, const22); + } + + public void jmpl(Register rs1, Register rs2, Register rd) { + insertNopAfterCBCond(); + op3(Jmpl, rs1, rs2, rd); + } + + /** + * @return Position of the jmpl instruction + */ + public int jmpl(Register rs1, int simm13, Register rd) { + insertNopAfterCBCond(); + int before = position(); + op3(Jmpl, rs1, simm13, rd); + return before; + } + + public void fmovdcc(ConditionFlag cond, CC cc, Register rs2, Register rd) { + fmovcc(cond, cc, rs2, rd, OpfLow.Fmovdcc.value); + } + + public void fmovscc(ConditionFlag cond, CC cc, Register rs2, Register rd) { + fmovcc(cond, cc, rs2, rd, OpfLow.Fmovscc.value); + } + + private void fmovcc(ConditionFlag cond, CC cc, Register rs2, Register rd, int opfLow) { + int opfCC = cc.value; + int a = opfCC << 11 | opfLow << 5 | rs2.encoding; + fmt10(rd.encoding, Fpop2.value, cond.value, a); + } + + public void movcc(ConditionFlag conditionFlag, CC cc, Register rs2, Register rd) { + movcc(conditionFlag, cc, 0, rs2.encoding, rd); + } + + public void movcc(ConditionFlag conditionFlag, CC cc, int simm11, Register rd) { + assert isSimm11(simm11); + movcc(conditionFlag, cc, 1, simm11 & ((1 << 11) - 1), rd); + } + + private void movcc(ConditionFlag conditionFlag, CC cc, int i, int imm, Register rd) { + int cc01 = 0b11 & cc.value; + int cc2 = cc.isFloat ? 0 : 1; + int a = cc2 << 4 | conditionFlag.value; + int b = cc01 << 11 | i << 13 | imm; + fmt10(rd.encoding, Movcc.value, a, b); + } + + public void mulx(Register rs1, Register rs2, Register rd) { + op3(Mulx, rs1, rs2, rd); + } + + public void mulx(Register rs1, int simm13, Register rd) { + op3(Mulx, rs1, simm13, rd); + } + + public void or(Register rs1, Register rs2, Register rd) { + assert isCPURegister(rs1, rs2, rd) : String.format("%s %s %s", rs1, rs2, rd); + op3(Or, rs1, rs2, rd); + } + + public void or(Register rs1, int simm13, Register rd) { + assert isCPURegister(rs1, rd) : String.format("%s %s", rs1, rd); + op3(Or, rs1, simm13, rd); + } + + public void popc(Register rs2, Register rd) { + op3(Popc, g0, rs2, rd); + } + + public void popc(int simm13, Register rd) { + op3(Popc, g0, simm13, rd); + } + + public void prefetch(SPARCAddress addr, Fcn fcn) { + Register rs1 = addr.getBase(); + if (addr.getIndex().equals(Register.None)) { + int dis = addr.getDisplacement(); + assert isSimm13(dis); + fmt(Prefetch.op.value, fcn.value, Prefetch.value, rs1.encoding, 1 << 13 | dis & ((1 << 13) - 1)); + } else { + Register rs2 = addr.getIndex(); + fmt(Prefetch.op.value, fcn.value, Prefetch.value, rs1.encoding, rs2.encoding); + } + } + + // A.44 Read State Register + + public void rdpc(Register rd) { + op3(Rd, g5, g0, rd); + } + + public void restore(Register rs1, Register rs2, Register rd) { + op3(Restore, rs1, rs2, rd); + } + + public static final int PC_RETURN_OFFSET = 8; + + public void save(Register rs1, Register rs2, Register rd) { + op3(Save, rs1, rs2, rd); + } + + public void save(Register rs1, int simm13, Register rd) { + op3(Save, rs1, simm13, rd); + } + + public void sdivx(Register rs1, Register rs2, Register rd) { + op3(Sdivx, rs1, rs2, rd); + } + + public void sdivx(Register rs1, int simm13, Register rd) { + op3(Sdivx, rs1, simm13, rd); + } + + public void udivx(Register rs1, Register rs2, Register rd) { + op3(Udivx, rs1, rs2, rd); + } + + public void udivx(Register rs1, int simm13, Register rd) { + op3(Udivx, rs1, simm13, rd); + } + + public void sll(Register rs1, Register rs2, Register rd) { + op3(Sll, rs1, rs2, rd); + } + + public void sll(Register rs1, int shcnt32, Register rd) { + assert isImm(shcnt32, 5); + op3(Sll, rs1, shcnt32, rd); + } + + public void sllx(Register rs1, Register rs2, Register rd) { + op3(Sllx, rs1, rs2, rd); + } + + public void sllx(Register rs1, int shcnt64, Register rd) { + assert isImm(shcnt64, 6); + op3(Sllx, rs1, shcnt64, rd); + } + + public void sra(Register rs1, Register rs2, Register rd) { + op3(Sra, rs1, rs2, rd); + } + + public void sra(Register rs1, int simm13, Register rd) { + op3(Sra, rs1, simm13, rd); + } + + public void srax(Register rs1, Register rs2, Register rd) { + op3(Srax, rs1, rs2, rd); + } + + public void srax(Register rs1, int shcnt64, Register rd) { + assert isImm(shcnt64, 6); + op3(Srax, rs1, shcnt64, rd); + } + + public void srl(Register rs1, Register rs2, Register rd) { + op3(Srl, rs1, rs2, rd); + } + + public void srl(Register rs1, int simm13, Register rd) { + op3(Srl, rs1, simm13, rd); + } + + public void srlx(Register rs1, Register rs2, Register rd) { + op3(Srlx, rs1, rs2, rd); + } + + public void srlx(Register rs1, int shcnt64, Register rd) { + assert isImm(shcnt64, 6); + op3(Srlx, rs1, shcnt64, rd); + } + + public void sub(Register rs1, Register rs2, Register rd) { + op3(Sub, rs1, rs2, rd); + } + + public void sub(Register rs1, int simm13, Register rd) { + op3(Sub, rs1, simm13, rd); + } + + public void subcc(Register rs1, Register rs2, Register rd) { + op3(Subcc, rs1, rs2, rd); + } + + public void subcc(Register rs1, int simm13, Register rd) { + op3(Subcc, rs1, simm13, rd); + } + + public void ta(int trap) { + tcc(Icc, Always, trap); + } + + public void pause() { + // Maybe fmt10(rd=0b1_1011, op3=0b11_0000, rs1=0, i=1, simm13=1), or + // maybe op3(Wr, g0, 1, %pause). + // What should the count be? + GraalError.unimplemented("The SPARC pause instruction is not yet implemented."); + } + + public void tcc(CC cc, ConditionFlag flag, int trap) { + assert isImm(trap, 8); + int b = cc.value << 11; + b |= 1 << 13; + b |= trap; + fmt10(flag.value, Op3s.Tcc.getValue(), 0, b); + } + + public void wrccr(Register rs1, Register rs2) { + op3(Wr, rs1, rs2, g2); + } + + public void wrccr(Register rs1, int simm13) { + op3(Wr, rs1, simm13, g2); + } + + public void xor(Register rs1, Register rs2, Register rd) { + op3(Xor, rs1, rs2, rd); + } + + public void xor(Register rs1, int simm13, Register rd) { + op3(Xor, rs1, simm13, rd); + } + + public void xorcc(Register rs1, Register rs2, Register rd) { + op3(Xorcc, rs1, rs2, rd); + } + + public void xorcc(Register rs1, int simm13, Register rd) { + op3(Xorcc, rs1, simm13, rd); + } + + public void xnor(Register rs1, Register rs2, Register rd) { + op3(Xnor, rs1, rs2, rd); + } + + public void xnor(Register rs1, int simm13, Register rd) { + op3(Xnor, rs1, simm13, rd); + } + + /* + * Load/Store + */ + protected void ld(Op3s op3, SPARCAddress addr, Register rd, Asi asi) { + Register rs1 = addr.getBase(); + if (!addr.getIndex().equals(Register.None)) { + Register rs2 = addr.getIndex(); + if (asi != null) { + int b = rs2.encoding; + b |= asi.value << 5; + fmt(op3.op.value, rd.encoding, op3.value, rs1.encoding, b); + } else { + op3(op3, rs1, rs2, rd); + } + } else { + int imm = addr.getDisplacement(); + op3(op3, rs1, imm, rd); + } + } + + protected void ld(Op3s op3, SPARCAddress addr, Register rd) { + ld(op3, addr, rd, null); + } + + public void lddf(SPARCAddress src, Register dst) { + assert isDoubleFloatRegister(dst) : dst; + ld(Lddf, src, dst); + } + + public void ldf(SPARCAddress src, Register dst) { + assert isSingleFloatRegister(dst) : dst; + ld(Ldf, src, dst); + } + + public void lduh(SPARCAddress src, Register dst) { + assert isCPURegister(dst) : dst; + ld(Lduh, src, dst); + } + + public void ldsh(SPARCAddress src, Register dst) { + assert isCPURegister(dst) : dst; + ld(Ldsh, src, dst); + } + + public void ld(SPARCAddress src, Register dst, int bytes, boolean signExtend) { + if (isCPURegister(dst)) { + if (signExtend) { + switch (bytes) { + case 1: + ld(Ldsb, src, dst); + break; + case 2: + ld(Ldsh, src, dst); + break; + case 4: + ld(Ldsw, src, dst); + break; + case 8: + ld(Ldx, src, dst); + break; + default: + throw new InternalError(); + } + } else { + switch (bytes) { + case 1: + ld(Ldub, src, dst); + break; + case 2: + ld(Lduh, src, dst); + break; + case 4: + ld(Lduw, src, dst); + break; + case 8: + ld(Ldx, src, dst); + break; + default: + throw new InternalError(); + } + } + } else if (isDoubleFloatRegister(dst) && bytes == 8) { + assert !signExtend; + ld(Lddf, src, dst); + } else if (isSingleFloatRegister(dst) && bytes == 4) { + assert !signExtend; + ld(Ldf, src, dst); + } else { + throw new InternalError(String.format("src: %s dst: %s bytes: %d signExtend: %b", src, dst, bytes, signExtend)); + } + } + + public void st(Register src, SPARCAddress dst, int bytes) { + if (isCPURegister(src)) { + switch (bytes) { + case 1: + st(Stb, src, dst); + break; + case 2: + st(Sth, src, dst); + break; + case 4: + st(Stw, src, dst); + break; + case 8: + st(Stx, src, dst); + break; + default: + throw new InternalError(Integer.toString(bytes)); + } + } else if (isDoubleFloatRegister(src) && bytes == 8) { + st(Stdf, src, dst); + } else if (isSingleFloatRegister(src) && bytes == 4) { + st(Stf, src, dst); + } else { + throw new InternalError(String.format("src: %s dst: %s bytes: %d", src, dst, bytes)); + } + } + + public void ldub(SPARCAddress src, Register dst) { + assert isCPURegister(dst) : dst; + ld(Ldub, src, dst); + } + + public void ldsb(SPARCAddress src, Register dst) { + assert isCPURegister(dst) : dst; + ld(Ldsb, src, dst); + } + + public void lduw(SPARCAddress src, Register dst) { + assert isCPURegister(dst) : dst; + ld(Lduw, src, dst); + } + + public void ldsw(SPARCAddress src, Register dst) { + assert isCPURegister(dst) : dst; + ld(Ldsw, src, dst); + } + + public void ldx(SPARCAddress src, Register dst) { + assert isCPURegister(dst) : dst; + ld(Ldx, src, dst); + } + + public void ldxa(Register rs1, Register rs2, Register rd, Asi asi) { + assert isCPURegister(rs1, rs2, rd) : format("%s %s %s", rs1, rs2, rd); + ld(Ldxa, new SPARCAddress(rs1, rs2), rd, asi); + } + + public void lduwa(Register rs1, Register rs2, Register rd, Asi asi) { + assert isCPURegister(rs1, rs2, rd) : format("%s %s %s", rs1, rs2, rd); + ld(Lduwa, new SPARCAddress(rs1, rs2), rd, asi); + } + + public void stxa(Register rd, Register rs1, Register rs2, Asi asi) { + assert isCPURegister(rs1, rs2, rd) : format("%s %s %s", rs1, rs2, rd); + ld(Stxa, new SPARCAddress(rs1, rs2), rd, asi); + } + + protected void st(Op3s op3, Register rs1, SPARCAddress dest) { + ld(op3, dest, rs1); + } + + public void stdf(Register rd, SPARCAddress addr) { + assert isDoubleFloatRegister(rd) : rd; + st(Stdf, rd, addr); + } + + public void stf(Register rd, SPARCAddress addr) { + assert isSingleFloatRegister(rd) : rd; + st(Stf, rd, addr); + } + + public void stb(Register rd, SPARCAddress addr) { + assert isCPURegister(rd) : rd; + st(Stb, rd, addr); + } + + public void sth(Register rd, SPARCAddress addr) { + assert isCPURegister(rd) : rd; + st(Sth, rd, addr); + } + + public void stw(Register rd, SPARCAddress addr) { + assert isCPURegister(rd) : rd; + st(Stw, rd, addr); + } + + public void stx(Register rd, SPARCAddress addr) { + assert isCPURegister(rd) : rd; + st(Stx, rd, addr); + } + + public void membar(int barriers) { + op3(Membar, o7, barriers, g0); + } + + public void casa(Register rs1, Register rs2, Register rd, Asi asi) { + ld(Casa, new SPARCAddress(rs1, rs2), rd, asi); + } + + public void casxa(Register rs1, Register rs2, Register rd, Asi asi) { + ld(Casxa, new SPARCAddress(rs1, rs2), rd, asi); + } + + @Override + public InstructionCounter getInstructionCounter() { + return new SPARCInstructionCounter(this); + } + + public void patchAddImmediate(int position, int simm13) { + int inst = getInt(position); + assert SPARCAssembler.isSimm13(simm13) : simm13; + assert (inst >>> 30) == 0b10 : String.format("0x%x", inst); + assert ((inst >>> 18) & 0b11_1111) == 0 : String.format("0x%x", inst); + assert (inst & (1 << 13)) != 0 : String.format("0x%x", inst); + inst = inst & (~((1 << 13) - 1)); + inst |= simm13 & ((1 << 12) - 1); + emitInt(inst, position); + } + + public void fpadd32(Register rs1, Register rs2, Register rd) { + op3(Impdep1, Fpadd32, rs1, rs2, rd); + } + + /** + * Does peephole optimization on code generated by this assembler. This method should be called + * at the end of code generation. + *

+ * It searches for conditional branch instructions which has nop in the delay slot then looks at + * the instruction at branch target; if it is an arithmetic instruction, which does not throw an + * exception (e.g. division), it pulls this instruction into the delay slot and increments the + * displacement by 1. + */ + public void peephole() { + for (int i : delaySlotOptimizationPoints) { + optimizeDelaySlot(i); + } + } + + /** + * Optimizes branch instruction b which has a nop in the delay slot. It tries to stuff + * the instruction at bs branch target into the delay slot of b, set the annul + * flag and increments bs disp field by 1; + *

+ * If bs branch target instruction is an unconditional branch t, then it tries to + * put ts delayed instruction into the delay slot of b and add the ts disp + * field to bs disp field. + */ + private void optimizeDelaySlot(int i) { + int delaySlotAbsolute = i + INSTRUCTION_SIZE; + int nextInst = getInt(delaySlotAbsolute); + SPARCOp nextOp = getSPARCOp(nextInst); + if (nextOp instanceof Sethi && Sethi.isNop(nextInst)) { + int inst = getInt(i); + SPARCOp op = getSPARCOp(inst); + if (op instanceof ControlTransferOp && ((ControlTransferOp) op).hasDelaySlot() && ((ControlTransferOp) op).isAnnulable(inst)) { + ControlTransferOp ctOp = (ControlTransferOp) op; + int disp = ctOp.getDisp(inst); + int branchTargetAbsolute = i + disp * INSTRUCTION_SIZE; + int branchTargetInst = getInt(branchTargetAbsolute); + SPARCOp branchTargetOp = getSPARCOp(branchTargetInst); + if (branchTargetOp instanceof Op3Op) { + Op3s op3 = ((Op3Op) branchTargetOp).getOp3(branchTargetInst); + if (!op3.throwsException()) { + inst = ctOp.setDisp(inst, disp + 1); // Increment the offset + inst = ctOp.setAnnul(inst, true); + emitInt(inst, i); + emitInt(branchTargetInst, delaySlotAbsolute); + } + } else if (branchTargetOp instanceof ControlTransferOp && !((ControlTransferOp) branchTargetOp).isConditional(branchTargetInst)) { + // If branchtarget is a unconditional branch + ControlTransferOp branchTargetOpBranch = (ControlTransferOp) branchTargetOp; + int btDisp = branchTargetOpBranch.getDisp(branchTargetInst); + int newDisp = disp + btDisp; + if (ctOp.isValidDisp(newDisp)) { // Test if we don't exceed field size + int instAfter = ctOp.setDisp(inst, newDisp); + instAfter = ctOp.setAnnul(instAfter, true); + branchTargetInst = getInt(branchTargetAbsolute + INSTRUCTION_SIZE); + branchTargetOp = getSPARCOp(branchTargetInst); + if (branchTargetOp instanceof Op3Op && !((Op3Op) branchTargetOp).getOp3(branchTargetInst).throwsException()) { + emitInt(instAfter, i); + emitInt(branchTargetInst, delaySlotAbsolute); + } + } + } + } + } + } +}