/* * 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); } } } } } } }