Mercurial > hg > truffle
diff graal/GraalCompiler/src/com/sun/c1x/target/amd64/AMD64GlobalStubEmitter.java @ 2509:16b9a8b5ad39
Renamings Runtime=>GraalRuntime and Compiler=>GraalCompiler
author | Thomas Wuerthinger <thomas@wuerthinger.net> |
---|---|
date | Wed, 27 Apr 2011 11:50:44 +0200 |
parents | graal/Compiler/src/com/sun/c1x/target/amd64/AMD64GlobalStubEmitter.java@9ec15d6914ca |
children | 0ea5f12e873a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/GraalCompiler/src/com/sun/c1x/target/amd64/AMD64GlobalStubEmitter.java Wed Apr 27 11:50:44 2011 +0200 @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2009, 2011, 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 com.sun.c1x.target.amd64; + +import static com.sun.cri.ci.CiCallingConvention.Type.*; + +import java.util.*; + +import com.sun.c1x.*; +import com.sun.c1x.asm.*; +import com.sun.c1x.globalstub.*; +import com.sun.c1x.target.amd64.AMD64Assembler.ConditionFlag; +import com.sun.cri.ci.*; +import com.sun.cri.ci.CiRegister.RegisterFlag; +import com.sun.cri.ri.*; +import com.sun.cri.xir.*; +import com.sun.cri.xir.CiXirAssembler.XirConstant; +import com.sun.cri.xir.CiXirAssembler.XirConstantOperand; +import com.sun.cri.xir.CiXirAssembler.XirOperand; +import com.sun.cri.xir.CiXirAssembler.XirParameter; +import com.sun.cri.xir.CiXirAssembler.XirRegister; +import com.sun.cri.xir.CiXirAssembler.XirTemp; + +public class AMD64GlobalStubEmitter implements GlobalStubEmitter { + + public static final int ARGUMENT_SIZE = 8; + + private static final long FloatSignFlip = 0x8000000080000000L; + private static final long DoubleSignFlip = 0x8000000000000000L; + private static final CiRegister convertArgument = AMD64.xmm0; + private static final CiRegister convertResult = AMD64.rax; + private static final CiRegister negateArgument = AMD64.xmm0; + private static final CiRegister negateTemp = AMD64.xmm1; + + private AMD64MacroAssembler asm; + private final CiTarget target; + private int argsSize; + private int[] argOffsets; + private int resultOffset; + private int saveSize; + private int registerRestoreEpilogueOffset; + + private RiRuntime runtime; + private C1XCompiler compiler; + private CiRegister[] registersSaved; + + private boolean savedAllRegisters; + + public AMD64GlobalStubEmitter(C1XCompiler compiler) { + this.compiler = compiler; + this.target = compiler.target; + this.runtime = compiler.runtime; + } + + private void reset(CiKind resultKind, CiKind[] argTypes) { + asm = new AMD64MacroAssembler.WithCompiler(compiler, compiler.globalStubRegisterConfig); + saveSize = 0; + argsSize = 0; + argOffsets = new int[argTypes.length]; + resultOffset = 0; + registerRestoreEpilogueOffset = -1; + registersSaved = null; + + for (int i = 0; i < argTypes.length; i++) { + argOffsets[i] = argsSize; + argsSize += ARGUMENT_SIZE; + } + + if (resultKind != CiKind.Void) { + if (argsSize == 0) { + argsSize = ARGUMENT_SIZE; + } + resultOffset = 0; + } + } + + public GlobalStub emit(CiRuntimeCall runtimeCall, RiRuntime runtime) { + reset(runtimeCall.resultKind, runtimeCall.arguments); + emitStandardForward(null, runtimeCall); + String name = "stub-" + runtimeCall; + CiTargetMethod targetMethod = asm.finishTargetMethod(name, runtime, registerRestoreEpilogueOffset, true); + Object stubObject = runtime.registerGlobalStub(targetMethod, name); + return new GlobalStub(null, runtimeCall.resultKind, stubObject, argsSize, argOffsets, resultOffset); + } + + public GlobalStub emit(GlobalStub.Id stub, RiRuntime runtime) { + reset(stub.resultKind, stub.arguments); + + switch (stub) { + case f2i: + emitF2I(); + break; + case f2l: + emitF2L(); + break; + case d2i: + emitD2I(); + break; + case d2l: + emitD2L(); + break; + case fneg: + emitFNEG(); + break; + case dneg: + emitDNEG(); + break; + } + + String name = "stub-" + stub; + CiTargetMethod targetMethod = asm.finishTargetMethod(name, runtime, registerRestoreEpilogueOffset, true); + Object stubObject = runtime.registerGlobalStub(targetMethod, name); + return new GlobalStub(stub, stub.resultKind, stubObject, argsSize, argOffsets, resultOffset); + } + + private CiValue allocateParameterOperand(XirParameter param, int parameterIndex) { + return new CiAddress(param.kind, AMD64.RSP, argumentIndexToStackOffset(parameterIndex)); + } + + private CiValue allocateResultOperand(XirOperand result) { + return new CiAddress(result.kind, AMD64.RSP, argumentIndexToStackOffset(0)); + } + + private CiValue allocateOperand(XirTemp temp, ArrayList<CiRegister> allocatableRegisters) { + if (temp instanceof XirRegister) { + XirRegister fixed = (XirRegister) temp; + return fixed.register; + } + + return newRegister(temp.kind, allocatableRegisters); + } + + private CiValue newRegister(CiKind kind, ArrayList<CiRegister> allocatableRegisters) { + assert kind != CiKind.Float && kind != CiKind.Double; + assert allocatableRegisters.size() > 0; + return allocatableRegisters.remove(allocatableRegisters.size() - 1).asValue(kind); + } + + public GlobalStub emit(XirTemplate template, RiRuntime runtime) { + C1XCompilation compilation = new C1XCompilation(compiler, null, -1, null); + try { + return emit(template, compilation); + } finally { + compilation.close(); + } + } + + public GlobalStub emit(XirTemplate template, C1XCompilation compilation) { + reset(template.resultOperand.kind, getArgumentKinds(template)); + compilation.initFrameMap(0); + compilation.frameMap().setFrameSize(frameSize()); + AMD64LIRAssembler assembler = new AMD64LIRAssembler(compilation); + asm = assembler.masm; + + ArrayList<CiRegister> allocatableRegisters = new ArrayList<CiRegister>(Arrays.asList(compiler.globalStubRegisterConfig.getCategorizedAllocatableRegisters().get(RegisterFlag.CPU))); + for (XirTemp t : template.temps) { + if (t instanceof XirRegister) { + final XirRegister fixed = (XirRegister) t; + if (fixed.register.isRegister()) { + allocatableRegisters.remove(fixed.register.asRegister()); + } + } + } + + completeSavePrologue(); + + CiValue[] operands = new CiValue[template.variableCount]; + + XirOperand resultOperand = template.resultOperand; + + if (template.allocateResultOperand) { + CiValue outputOperand = CiValue.IllegalValue; + // This snippet has a result that must be separately allocated + // Otherwise it is assumed that the result is part of the inputs + if (resultOperand.kind != CiKind.Void && resultOperand.kind != CiKind.Illegal) { + outputOperand = allocateResultOperand(resultOperand); + assert operands[resultOperand.index] == null; + } + operands[resultOperand.index] = outputOperand; + } + + for (XirParameter param : template.parameters) { + assert !(param instanceof XirConstantOperand) : "constant parameters not supported for stubs"; + CiValue op = allocateParameterOperand(param, param.parameterIndex); + assert operands[param.index] == null; + + // Is the value destroyed? + if (template.isParameterDestroyed(param.parameterIndex)) { + CiValue newOp = newRegister(op.kind, allocatableRegisters); + assembler.moveOp(op, newOp, op.kind, null, false); + operands[param.index] = newOp; + } else { + operands[param.index] = op; + } + } + + for (XirConstant c : template.constants) { + assert operands[c.index] == null; + operands[c.index] = c.value; + } + + for (XirTemp t : template.temps) { + CiValue op = allocateOperand(t, allocatableRegisters); + assert operands[t.index] == null; + operands[t.index] = op; + } + + for (CiValue operand : operands) { + assert operand != null; + } + + Label[] labels = new Label[template.labels.length]; + for (int i = 0; i < labels.length; i++) { + labels[i] = new Label(); + } + + assert template.marks.length == 0 : "marks not supported in global stubs"; + assembler.emitXirInstructions(null, template.fastPath, labels, operands, null); + epilogue(); + CiTargetMethod targetMethod = asm.finishTargetMethod(template.name, runtime, registerRestoreEpilogueOffset, true); + Object stubObject = runtime.registerGlobalStub(targetMethod, template.name); + return new GlobalStub(null, template.resultOperand.kind, stubObject, argsSize, argOffsets, resultOffset); + } + + private CiKind[] getArgumentKinds(XirTemplate template) { + CiXirAssembler.XirParameter[] params = template.parameters; + CiKind[] result = new CiKind[params.length]; + for (int i = 0; i < params.length; i++) { + result[i] = params[i].kind; + } + return result; + } + + private void negatePrologue() { + partialSavePrologue(negateArgument, negateTemp); + loadArgument(0, negateArgument); + } + + private void negateEpilogue() { + storeArgument(0, negateArgument); + epilogue(); + } + + private void emitDNEG() { + negatePrologue(); + asm.movsd(negateTemp, asm.recordDataReferenceInCode(CiConstant.forLong(DoubleSignFlip))); + asm.xorpd(negateArgument, negateTemp); + negateEpilogue(); + } + + private void emitFNEG() { + negatePrologue(); + asm.movsd(negateTemp, asm.recordDataReferenceInCode(CiConstant.forLong(FloatSignFlip))); + asm.xorps(negateArgument, negateTemp); + negateEpilogue(); + } + + private void convertPrologue() { + partialSavePrologue(convertArgument, convertResult); + loadArgument(0, convertArgument); + } + + private void convertEpilogue() { + storeArgument(0, convertResult); + epilogue(); + } + + private void emitD2L() { + emitCOMISSD(true, false); + } + + private void emitD2I() { + emitCOMISSD(true, true); + } + + private void emitF2L() { + emitCOMISSD(false, false); + } + + private void emitF2I() { + emitCOMISSD(false, true); + } + + private void emitCOMISSD(boolean isDouble, boolean isInt) { + convertPrologue(); + if (isDouble) { + asm.ucomisd(convertArgument, asm.recordDataReferenceInCode(CiConstant.DOUBLE_0)); + } else { + asm.ucomiss(convertArgument, asm.recordDataReferenceInCode(CiConstant.FLOAT_0)); + } + Label nan = new Label(); + Label ret = new Label(); + asm.jccb(ConditionFlag.parity, nan); + asm.jccb(ConditionFlag.below, ret); + + // input is > 0 -> return maxInt + // result register already contains 0x80000000, so subtracting 1 gives 0x7fffffff + asm.decrementl(convertResult, 1); + asm.jmpb(ret); + + // input is NaN -> return 0 + asm.bind(nan); + asm.xorptr(convertResult, convertResult); + + asm.bind(ret); + convertEpilogue(); + } + + private void emitStandardForward(GlobalStub.Id stub, CiRuntimeCall call) { + if (stub != null) { + assert stub.resultKind == call.resultKind; + assert stub.arguments.length == call.arguments.length; + for (int i = 0; i < stub.arguments.length; i++) { + assert stub.arguments[i] == call.arguments[i]; + } + } + + completeSavePrologue(); + forwardRuntimeCall(call); + epilogue(); + } + + private int argumentIndexToStackOffset(int index) { + // <-- lower addresses + // | stub frame | caller frame | + // | locals,savearea,retaddr | args ..... | + return frameSize() + (index + 1) * ARGUMENT_SIZE; + } + + private void loadArgument(int index, CiRegister register) { + asm.movq(register, new CiAddress(CiKind.Word, AMD64.RSP, argumentIndexToStackOffset(index))); + } + + private void storeArgument(int index, CiRegister register) { + asm.movq(new CiAddress(CiKind.Word, AMD64.RSP, argumentIndexToStackOffset(index)), register); + } + + private void partialSavePrologue(CiRegister... registersToSave) { + this.registersSaved = registersToSave; + this.saveSize = registersToSave.length * target.wordSize; + + // align to code size + int entryCodeOffset = runtime.codeOffset(); + if (entryCodeOffset != 0) { + asm.nop(entryCodeOffset); + } + asm.subq(AMD64.rsp, frameSize()); + + int index = 0; + for (CiRegister r : registersToSave) { + asm.movq(new CiAddress(CiKind.Word, AMD64.RSP, index * target.arch.wordSize), r); + index++; + } + + asm.setFrameSize(frameSize()); + this.savedAllRegisters = false; + } + + private void completeSavePrologue() { + CiCalleeSaveArea csa = compiler.globalStubRegisterConfig.getCalleeSaveArea(); + this.saveSize = csa.size; + int entryCodeOffset = runtime.codeOffset(); + if (entryCodeOffset != 0) { + // align to code size + asm.nop(entryCodeOffset); + } + asm.subq(AMD64.rsp, frameSize()); + asm.setFrameSize(frameSize()); + int frameToCSA = 0; + asm.save(csa, frameToCSA); + this.savedAllRegisters = true; + } + + private void epilogue() { + assert registerRestoreEpilogueOffset == -1; + registerRestoreEpilogueOffset = asm.codeBuffer.position(); + + if (savedAllRegisters) { + CiCalleeSaveArea csa = compiler.globalStubRegisterConfig.getCalleeSaveArea(); + int frameToCSA = 0; + asm.restore(csa, frameToCSA); + } else { + // saved only select registers + for (int index = 0; index < registersSaved.length; index++) { + CiRegister r = registersSaved[index]; + asm.movq(r, new CiAddress(CiKind.Word, AMD64.RSP, index * target.wordSize)); + } + registersSaved = null; + } + + // Restore rsp + asm.addq(AMD64.rsp, frameSize()); + asm.ret(0); + } + + private int frameSize() { + return target.alignFrameSize(saveSize); + } + + private void forwardRuntimeCall(CiRuntimeCall call) { + // Load arguments + CiCallingConvention cc = compiler.globalStubRegisterConfig.getCallingConvention(RuntimeCall, call.arguments, target); + for (int i = 0; i < cc.locations.length; ++i) { + CiValue location = cc.locations[i]; + loadArgument(i, location.asRegister()); + } + + // Call to the runtime + asm.directCall(call, null); + + if (call.resultKind != CiKind.Void) { + CiRegister returnRegister = compiler.globalStubRegisterConfig.getReturnRegister(call.resultKind); + this.storeArgument(0, returnRegister); + } + } +}