blob: 4761511836198b7ef36d2caf23cf48cc1652fc6e [file] [log] [blame]
/*
* 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
}
@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();
}
}
}
}