| /* |
| * Copyright (c) 2013, 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.Annul.NOT_ANNUL; |
| import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_NOT_TAKEN; |
| import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Icc; |
| import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Xcc; |
| import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Always; |
| import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Equal; |
| import static org.graalvm.compiler.asm.sparc.SPARCAssembler.RCondition.Rc_z; |
| import static jdk.vm.ci.sparc.SPARC.g0; |
| import static jdk.vm.ci.sparc.SPARC.g3; |
| import static jdk.vm.ci.sparc.SPARC.i7; |
| import static jdk.vm.ci.sparc.SPARC.o7; |
| |
| import org.graalvm.compiler.asm.AbstractAddress; |
| import org.graalvm.compiler.asm.Label; |
| |
| import jdk.vm.ci.code.Register; |
| import jdk.vm.ci.code.TargetDescription; |
| import jdk.vm.ci.sparc.SPARC.CPUFeature; |
| |
| public class SPARCMacroAssembler extends SPARCAssembler { |
| |
| /** |
| * A sentinel value used as a place holder in an instruction stream for an address that will be |
| * patched. |
| */ |
| private static final SPARCAddress Placeholder = new SPARCAddress(g0, 0); |
| private final ScratchRegister[] scratchRegister = new ScratchRegister[]{new ScratchRegister(g3), new ScratchRegister(o7)}; |
| // Points to the next free scratch register |
| private int nextFreeScratchRegister = 0; |
| /** |
| * Use ld [reg+simm13], reg for loading constants (User has to make sure, that the size of the |
| * constant table does not exceed simm13). |
| */ |
| private boolean immediateConstantLoad; |
| |
| public SPARCMacroAssembler(TargetDescription target) { |
| super(target); |
| } |
| |
| /** |
| * @see #immediateConstantLoad |
| */ |
| public void setImmediateConstantLoad(boolean immediateConstantLoad) { |
| this.immediateConstantLoad = immediateConstantLoad; |
| } |
| |
| @Override |
| public void align(int modulus) { |
| while (position() % modulus != 0) { |
| nop(); |
| } |
| } |
| |
| @Override |
| public void jmp(Label l) { |
| BPCC.emit(this, Xcc, Always, NOT_ANNUL, PREDICT_NOT_TAKEN, l); |
| nop(); // delay slot |
| } |
| |
| public void bz(Label l) { |
| BPCC.emit(this, Xcc, ConditionFlag.Zero, NOT_ANNUL, PREDICT_NOT_TAKEN, l); |
| } |
| |
| @Override |
| protected final void patchJumpTarget(int branch, int branchTarget) { |
| final int disp = (branchTarget - branch) / 4; |
| final int inst = getInt(branch); |
| ControlTransferOp op = (ControlTransferOp) getSPARCOp(inst); |
| int newInst = op.setDisp(inst, disp); |
| emitInt(newInst, branch); |
| } |
| |
| @Override |
| public AbstractAddress makeAddress(Register base, int displacement) { |
| return new SPARCAddress(base, displacement); |
| } |
| |
| @Override |
| public AbstractAddress getPlaceholder(int instructionStartPosition) { |
| return Placeholder; |
| } |
| |
| @Override |
| public final void ensureUniquePC() { |
| nop(); |
| } |
| |
| public void cas(Register rs1, Register rs2, Register rd) { |
| casa(rs1, rs2, rd, Asi.ASI_PRIMARY); |
| } |
| |
| public void casx(Register rs1, Register rs2, Register rd) { |
| casxa(rs1, rs2, rd, Asi.ASI_PRIMARY); |
| } |
| |
| public void clr(Register dst) { |
| or(g0, g0, dst); |
| } |
| |
| public void clrb(SPARCAddress addr) { |
| stb(g0, addr); |
| } |
| |
| public void clrh(SPARCAddress addr) { |
| sth(g0, addr); |
| } |
| |
| public void clrx(SPARCAddress addr) { |
| stx(g0, addr); |
| } |
| |
| public void cmp(Register rs1, Register rs2) { |
| subcc(rs1, rs2, g0); |
| } |
| |
| public void cmp(Register rs1, int simm13) { |
| subcc(rs1, simm13, g0); |
| } |
| |
| public void dec(Register rd) { |
| sub(rd, 1, rd); |
| } |
| |
| public void dec(int simm13, Register rd) { |
| sub(rd, simm13, rd); |
| } |
| |
| public void jmp(SPARCAddress address) { |
| jmpl(address.getBase(), address.getDisplacement(), g0); |
| } |
| |
| public void jmp(Register rd) { |
| jmpl(rd, 0, g0); |
| } |
| |
| public void neg(Register rs1, Register rd) { |
| sub(g0, rs1, rd); |
| } |
| |
| public void neg(Register rd) { |
| sub(g0, rd, rd); |
| } |
| |
| public void mov(Register rs, Register rd) { |
| or(g0, rs, rd); |
| } |
| |
| public void mov(int simm13, Register rd) { |
| or(g0, simm13, rd); |
| } |
| |
| public void not(Register rs1, Register rd) { |
| xnor(rs1, g0, rd); |
| } |
| |
| public void not(Register rd) { |
| xnor(rd, g0, rd); |
| } |
| |
| public void restoreWindow() { |
| restore(g0, g0, g0); |
| } |
| |
| public void ret() { |
| jmpl(i7, 8, g0); |
| } |
| |
| /** |
| * Generates sethi hi22(value), dst; or dst, lo10(value), dst; code. |
| */ |
| public void setw(int value, Register dst, boolean forceRelocatable) { |
| if (!forceRelocatable && isSimm13(value)) { |
| or(g0, value, dst); |
| } else { |
| sethi(hi22(value), dst); |
| or(dst, lo10(value), dst); |
| } |
| } |
| |
| public void setx(long value, Register dst, boolean forceRelocatable) { |
| int lo = (int) (value & ~0); |
| sethix(value, dst, forceRelocatable); |
| if (lo10(lo) != 0 || forceRelocatable) { |
| add(dst, lo10(lo), dst); |
| } |
| } |
| |
| public void sethix(long value, Register dst, boolean forceRelocatable) { |
| final int hi = (int) (value >> 32); |
| final int lo = (int) (value & ~0); |
| |
| // This is the same logic as MacroAssembler::internal_set. |
| final int startPc = position(); |
| if (hi == 0 && lo >= 0) { |
| sethi(hi22(lo), dst); |
| } else if (hi == -1) { |
| sethi(hi22(~lo), dst); |
| xor(dst, ~lo10(~0), dst); |
| } else { |
| final int shiftcnt; |
| final int shiftcnt2; |
| sethi(hi22(hi), dst); |
| if ((hi & 0x3ff) != 0) { // Any bits? |
| // msb 32-bits are now in lsb 32 |
| or(dst, hi & 0x3ff, dst); |
| } |
| if ((lo & 0xFFFFFC00) != 0) { // done? |
| if (((lo >> 20) & 0xfff) != 0) { // Any bits set? |
| // Make room for next 12 bits |
| sllx(dst, 12, dst); |
| // Or in next 12 |
| or(dst, (lo >> 20) & 0xfff, dst); |
| shiftcnt = 0; // We already shifted |
| } else { |
| shiftcnt = 12; |
| } |
| if (((lo >> 10) & 0x3ff) != 0) { |
| // Make room for last 10 bits |
| sllx(dst, shiftcnt + 10, dst); |
| // Or in next 10 |
| or(dst, (lo >> 10) & 0x3ff, dst); |
| shiftcnt2 = 0; |
| } else { |
| shiftcnt2 = 10; |
| } |
| // Shift leaving disp field 0'd |
| sllx(dst, shiftcnt2 + 10, dst); |
| } else { |
| sllx(dst, 32, dst); |
| } |
| } |
| // Pad out the instruction sequence so it can be patched later. |
| if (forceRelocatable) { |
| while (position() < (startPc + (INSTRUCTION_SIZE * 7))) { |
| nop(); |
| } |
| } |
| } |
| |
| public void signx(Register rs, Register rd) { |
| sra(rs, g0, rd); |
| } |
| |
| public void signx(Register rd) { |
| sra(rd, g0, rd); |
| } |
| |
| public boolean isImmediateConstantLoad() { |
| return immediateConstantLoad; |
| } |
| |
| public ScratchRegister getScratchRegister() { |
| return scratchRegister[nextFreeScratchRegister++]; |
| } |
| |
| public class ScratchRegister implements AutoCloseable { |
| private final Register register; |
| |
| public ScratchRegister(Register register) { |
| super(); |
| this.register = register; |
| } |
| |
| public Register getRegister() { |
| return register; |
| } |
| |
| @Override |
| public void close() { |
| assert nextFreeScratchRegister > 0 : "Close called too often"; |
| nextFreeScratchRegister--; |
| } |
| } |
| |
| public void compareBranch(Register rs1, Register rs2, ConditionFlag cond, CC ccRegister, Label label, BranchPredict predict, Runnable delaySlotInstruction) { |
| assert isCPURegister(rs1, rs2); |
| assert ccRegister == Icc || ccRegister == Xcc; |
| if (hasFeature(CPUFeature.CBCOND)) { |
| if (delaySlotInstruction != null) { |
| delaySlotInstruction.run(); |
| } |
| CBCOND.emit(this, cond, ccRegister == Xcc, rs1, rs2, label); |
| } else { |
| if (cond == Equal && rs1.equals(g0)) { |
| BPR.emit(this, Rc_z, NOT_ANNUL, predict, rs1, label); |
| } else { |
| cmp(rs1, rs2); |
| BPCC.emit(this, ccRegister, cond, NOT_ANNUL, predict, label); |
| } |
| if (delaySlotInstruction != null) { |
| int positionBefore = position(); |
| delaySlotInstruction.run(); |
| int positionAfter = position(); |
| assert positionBefore - positionAfter > INSTRUCTION_SIZE : "Emitted more than one instruction into delay slot"; |
| } else { |
| nop(); |
| } |
| } |
| } |
| |
| public void compareBranch(Register rs1, int simm, ConditionFlag cond, CC ccRegister, Label label, BranchPredict predict, Runnable delaySlotInstruction) { |
| assert isCPURegister(rs1); |
| assert ccRegister == Icc || ccRegister == Xcc; |
| if (hasFeature(CPUFeature.CBCOND)) { |
| if (delaySlotInstruction != null) { |
| delaySlotInstruction.run(); |
| } |
| CBCOND.emit(this, cond, ccRegister == Xcc, rs1, simm, label); |
| } else { |
| if (cond == Equal && simm == 0) { |
| BPR.emit(this, Rc_z, NOT_ANNUL, PREDICT_NOT_TAKEN, rs1, label); |
| } else { |
| cmp(rs1, simm); |
| BPCC.emit(this, ccRegister, cond, NOT_ANNUL, predict, label); |
| } |
| if (delaySlotInstruction != null) { |
| int positionBefore = position(); |
| delaySlotInstruction.run(); |
| int positionAfter = position(); |
| assert positionBefore - positionAfter > INSTRUCTION_SIZE : "Emitted more than one instruction into delay slot"; |
| } else { |
| nop(); |
| } |
| } |
| } |
| } |