view c1x4hotspotsrc/HotSpotVM/src/com/sun/hotspot/c1x/HotSpotXirGenerator.java @ 1429:abc670a709dc

* -XX:TraceC1X=0...5 controls the native c1x tracing * -Dc1x.debug=true turns on the logging proxies and lots of log output on the java side * provide more information about types to the compiler (type hierarchy, etc) * provide exception handler tables to the compiler * add exception handlers to the nmethod * correct implementation of ExceptionObject * exception handling/unwinding entry points * modified versions of handle/unwind exception stubs using standard calling conventions * exception throwing * implicit null pointer exception, implicit div by 0 exception * arraystore/classcast/arrayindex exceptions * checkcast implementation * newarray, anewarray, multinewarray implementation * correct new instance initialization * access to java class mirrors (for ldc) * unresolved methods * class resolving - class patching (asssembly prototype copying)
author Lukas Stadler <lukas.stadler@oracle.com>
date Tue, 31 Aug 2010 22:13:30 -0700
parents 695451afc619
children 949ade3f2ff3
line wrap: on
line source

/*
 * Copyright (c) 2009-2010 Sun Microsystems, Inc. All rights reserved.
 *
 * Sun Microsystems, Inc. has intellectual property rights relating to technology embodied in the product that is
 * described in this document. In particular, and without limitation, these intellectual property rights may include one
 * or more of the U.S. patents listed at http://www.sun.com/patents and one or more additional patents or pending patent
 * applications in the U.S. and in other countries.
 *
 * U.S. Government Rights - Commercial software. Government users are subject to the Sun Microsystems, Inc. standard
 * license agreement and applicable provisions of the FAR and its supplements.
 *
 * Use is subject to license terms. Sun, Sun Microsystems, the Sun logo, Java and Solaris are trademarks or registered
 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries. All SPARC trademarks are used under license and
 * are trademarks or registered trademarks of SPARC International, Inc. in the U.S. and other countries.
 *
 * UNIX is a registered trademark in the U.S. and other countries, exclusively licensed through X/Open Company, Ltd.
 */
package com.sun.hotspot.c1x;

import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;

import com.sun.c1x.target.amd64.*;
import com.sun.cri.ci.CiAddress.Scale;
import com.sun.cri.ci.*;
import com.sun.cri.ri.*;
import com.sun.cri.ri.RiType.Representation;
import com.sun.cri.xir.*;
import com.sun.cri.xir.CiXirAssembler.XirOperand;
import com.sun.cri.xir.CiXirAssembler.*;

/**
 *
 * @author Thomas Wuerthinger, Lukas Stadler
 */
public class HotSpotXirGenerator implements RiXirGenerator {

    // this needs to correspond to c1x_CodeInstaller.hpp
    // @formatter:off
    private static final Integer MARK_VERIFIED_ENTRY            = 0x0001;
    private static final Integer MARK_UNVERIFIED_ENTRY          = 0x0002;
    private static final Integer MARK_OSR_ENTRY                 = 0x0003;
    private static final Integer MARK_UNWIND_ENTRY              = 0x0004;
    private static final Integer MARK_EXCEPTION_HANDLER_ENTRY   = 0x0005;

    private static final Integer MARK_STATIC_CALL_STUB          = 0x1000;

    private static final Integer MARK_INVOKE_INVALID            = 0x2000;
    private static final Integer MARK_INVOKEINTERFACE           = 0x2001;
    private static final Integer MARK_INVOKESTATIC              = 0x2002;
    private static final Integer MARK_INVOKESPECIAL             = 0x2003;
    private static final Integer MARK_INVOKEVIRTUAL             = 0x2004;

    private static final Integer MARK_IMPLICIT_NULL             = 0x3000;

    private static final Integer MARK_KLASS_PATCHING            = 0x4000;
    private static final Integer MARK_DUMMY_OOP_RELOCATION      = 0x4001;
    // @formatter:on

    private final HotSpotVMConfig config;
    private final CiTarget target;
    private final RiRegisterConfig registerConfig;

    private CiXirAssembler asm;

    private XirTemplate[] emptyTemplates = new XirTemplate[CiKind.values().length];
    private XirTemplate[] arrayLoadTemplates = new XirTemplate[CiKind.values().length];
    private XirTemplate[] arrayStoreTemplates = new XirTemplate[CiKind.values().length];
    private XirTemplate[] arrayLoadTemplatesWithLength = new XirTemplate[CiKind.values().length];
    private XirTemplate[] arrayStoreTemplatesWithLength = new XirTemplate[CiKind.values().length];
    private XirTemplate prologueTemplate;
    private XirTemplate staticPrologueTemplate;
    private XirTemplate epilogueTemplate;
    private XirTemplate arrayLengthTemplate;
    private XirTemplate safepointTemplate;
    private XirTemplate exceptionObjectTemplate;
    private XirTemplate invokeStaticTemplate;
    private XirTemplate invokeSpecialTemplate;
    private XirTemplate invokeInterfaceTemplate;
    private XirTemplate invokeVirtualTemplate;

    private XirTemplate newInstanceUnresolvedTemplate;
    private XirPair newObjectArrayTemplate;
    private XirTemplate newTypeArrayTemplate;
    private XirPair resolveClassTemplate;
    private XirPair checkCastTemplate;

    static class XirPair {

        final XirTemplate resolved;
        final XirTemplate unresolved;

        XirPair(XirTemplate resolved, XirTemplate unresolved) {
            this.resolved = resolved;
            this.unresolved = unresolved;
        }
    }

    private XirPair[] putFieldTemplates = new XirPair[CiKind.values().length];
    private XirPair[] getFieldTemplates = new XirPair[CiKind.values().length];
    private XirPair[] putStaticTemplates = new XirPair[CiKind.values().length];
    private XirPair[] getStaticTemplates = new XirPair[CiKind.values().length];
    private XirPair instanceofTemplate;
    private XirPair instanceofTemplateNonnull;

    public HotSpotXirGenerator(HotSpotVMConfig config, CiTarget target, RiRegisterConfig registerConfig) {
        this.config = config;
        this.target = target;
        this.registerConfig = registerConfig;
    }

    @Override
    public List<XirTemplate> buildTemplates(CiXirAssembler asm) {
        this.asm = asm;

        List<XirTemplate> templates = new ArrayList<XirTemplate>();
        for (CiKind kind : CiKind.JAVA_VALUES) {
            int index = kind.ordinal();

            if (kind == CiKind.Void) {
                asm.restart(CiKind.values()[index]);
                emptyTemplates[index] = asm.finishTemplate("empty-" + kind);
            } else {
                asm.restart();
                XirOperand result = asm.createTemp("result", kind);
                emptyTemplates[index] = asm.finishTemplate(result, "empty-" + kind);

                putFieldTemplates[index] = buildPutFieldTemplate(kind, kind == CiKind.Object);
                getFieldTemplates[index] = buildGetFieldTemplate(kind);
                putStaticTemplates[index] = buildPutStaticTemplate(kind, kind == CiKind.Object);
                getStaticTemplates[index] = buildGetStaticTemplate(kind);
                arrayLoadTemplates[index] = buildArrayLoad(kind, asm, true, false);
                arrayStoreTemplates[index] = buildArrayStore(kind, asm, true, kind == CiKind.Object, kind == CiKind.Object, false);
                arrayLoadTemplatesWithLength[index] = buildArrayLoad(kind, asm, true, true);
                arrayStoreTemplatesWithLength[index] = buildArrayStore(kind, asm, true, kind == CiKind.Object, kind == CiKind.Object, true);
                // newArrayTemplates[index] = buildNewArray(kind);
            }
            // templates.add(emptyTemplates[index]);
        }

        asm.restart();
        XirOperand result = asm.createTemp("result", CiKind.Word);
        emptyTemplates[CiKind.Word.ordinal()] = asm.finishTemplate(result, "empty-Word");

        prologueTemplate = buildPrologue(false);
        staticPrologueTemplate = buildPrologue(true);
        epilogueTemplate = buildEpilogue();
        arrayLengthTemplate = buildArrayLength();
        exceptionObjectTemplate = buildExceptionObject();
        safepointTemplate = buildSafepoint();
        instanceofTemplate = buildInstanceof(false);
        instanceofTemplateNonnull = buildInstanceof(true);
        invokeStaticTemplate = buildInvokeStatic();
        invokeSpecialTemplate = buildInvokeSpecial();
        invokeInterfaceTemplate = buildInvokeInterface();
        invokeVirtualTemplate = buildInvokeVirtual();
        newInstanceUnresolvedTemplate = buildNewInstanceUnresolved();
        newObjectArrayTemplate = new XirPair(buildNewObjectArray(true), buildNewObjectArray(false));
        newTypeArrayTemplate = buildNewTypeArray();
        resolveClassTemplate = new XirPair(buildResolveClass(true), buildResolveClass(false));
        checkCastTemplate = buildCheckCast();

        return templates;
    }

    private final OndemandTemplates<XirTemplate> newInstanceTemplates = new OndemandTemplates<XirTemplate>() {

        @Override
        protected XirTemplate create(CiXirAssembler asm, int size) {
            return buildNewInstance(asm, size);
        }
    };

    private final OndemandTemplates<XirPair> multiNewArrayTemplate = new OndemandTemplates<HotSpotXirGenerator.XirPair>() {

        @Override
        protected XirPair create(CiXirAssembler asm, int dimensions) {
            return new XirPair(buildMultiNewArray(dimensions, true), buildMultiNewArray(dimensions, false));
        }
    };

    private XirTemplate buildPrologue(boolean staticMethod) {
        asm.restart(CiKind.Void);
        XirOperand temp = asm.createRegister("temp (rax)", CiKind.Int, AMD64.rax);
        XirOperand framePointer = asm.createRegister("frame pointer", CiKind.Word, AMD64.rbp);
        XirOperand stackPointer = asm.createRegister("stack pointer", CiKind.Word, AMD64.rsp);

        asm.align(config.codeEntryAlignment);
        asm.mark(MARK_OSR_ENTRY);
        asm.mark(MARK_UNVERIFIED_ENTRY);
        if (!staticMethod) {
            // TODO do some checking...
            asm.add(temp, temp, asm.i(1));
            asm.sub(temp, temp, asm.i(1));
            asm.shouldNotReachHere();

            asm.align(config.codeEntryAlignment);
        }
        asm.mark(MARK_VERIFIED_ENTRY);
        asm.push(framePointer);
        asm.mov(framePointer, stackPointer);
        asm.pushFrame();

        // -- out of line -------------------------------------------------------
        XirOperand thread = asm.createRegister("thread", CiKind.Word, AMD64.r15);
        XirOperand exceptionOop = asm.createTemp("exception oop", CiKind.Object);
        XirLabel unwind = asm.createOutOfLineLabel("unwind");
        asm.bindOutOfLine(unwind);

        asm.mark(MARK_UNWIND_ENTRY);
        //asm.popFrame();
        //asm.pop(framePointer);

        // TODO synchronized methods / monitors

        asm.pload(CiKind.Object, exceptionOop, thread, asm.i(config.threadExceptionOopOffset), false);
        asm.pstore(CiKind.Object, thread, asm.i(config.threadExceptionOopOffset), asm.createConstant(CiConstant.NULL_OBJECT), false);
        asm.pstore(CiKind.Long, thread, asm.i(config.threadExceptionPcOffset), asm.l(0), false);

        asm.callRuntime(config.unwindExceptionStub, null, exceptionOop);
        asm.shouldNotReachHere();

        asm.mark(MARK_EXCEPTION_HANDLER_ENTRY);
        asm.callRuntime(config.handleExceptionStub, null);
        asm.shouldNotReachHere();

        return asm.finishTemplate(staticMethod ? "static prologue" : "prologue");
    }

    private XirTemplate buildEpilogue() {
        asm.restart(CiKind.Void);
        XirOperand framePointer = asm.createRegister("frame pointer", CiKind.Word, AMD64.rbp);

        asm.popFrame();
        asm.pop(framePointer);
        // TODO safepoint check

        return asm.finishTemplate("epilogue");
    }

    private XirTemplate buildArrayLength() {
        XirOperand result = asm.restart(CiKind.Int);
        XirParameter object = asm.createInputParameter("object", CiKind.Object);
        asm.mark(MARK_IMPLICIT_NULL);
        asm.pload(CiKind.Int, result, object, asm.i(config.arrayLengthOffset), true);
        return asm.finishTemplate("arrayLength");
    }

    private XirTemplate buildExceptionObject() {
        XirOperand result = asm.restart(CiKind.Object);
        XirOperand thread = asm.createRegister("thread", CiKind.Word, AMD64.r15);

        asm.pload(CiKind.Object, result, thread, asm.i(config.threadExceptionOopOffset), false);
        asm.pstore(CiKind.Object, thread, asm.i(config.threadExceptionOopOffset), asm.o(null), false);
        asm.pstore(CiKind.Long, thread, asm.i(config.threadExceptionPcOffset), asm.l(0), false);

        return asm.finishTemplate("exception object");
    }

    private XirTemplate buildSafepoint() {
        asm.restart(CiKind.Void);

        // TODO safepoint

        return asm.finishTemplate("safepoint");
    }

    private XirPair buildGetFieldTemplate(CiKind kind) {
        final XirTemplate resolved;
        final XirTemplate unresolved;
        {
            // resolved case
            XirOperand result = asm.restart(kind);
            XirParameter object = asm.createInputParameter("object", CiKind.Object);
            XirParameter fieldOffset = asm.createConstantInputParameter("fieldOffset", CiKind.Int);
            asm.mark(MARK_IMPLICIT_NULL);
            asm.pload(kind, result, object, fieldOffset, true);
            resolved = asm.finishTemplate("getfield<" + kind + ">");
        }
        unresolved = null;
        return new XirPair(resolved, unresolved);
    }

    private XirPair buildPutFieldTemplate(CiKind kind, boolean genWriteBarrier) {
        final XirTemplate resolved;
        final XirTemplate unresolved;
        {
            // resolved case
            asm.restart(CiKind.Void);
            XirParameter object = asm.createInputParameter("object", CiKind.Object);
            XirParameter value = asm.createInputParameter("value", kind);
            XirParameter fieldOffset = asm.createConstantInputParameter("fieldOffset", CiKind.Int);
            asm.mark(MARK_IMPLICIT_NULL);
            asm.pstore(kind, object, fieldOffset, value, true);
            if (genWriteBarrier) {
                // TODO write barrier
                // addWriteBarrier(asm, object, value);
            }
            resolved = asm.finishTemplate("putfield<" + kind + ", " + genWriteBarrier + ">");
        }
        unresolved = null;
        return new XirPair(resolved, unresolved);
    }

    private XirPair buildGetStaticTemplate(CiKind kind) {
        final XirTemplate resolved;
        final XirTemplate unresolved;
        {
            // resolved case
            XirOperand result = asm.restart(kind);
            XirParameter object = asm.createInputParameter("object", CiKind.Object);
            XirParameter fieldOffset = asm.createConstantInputParameter("fieldOffset", CiKind.Int);
            asm.mark(MARK_IMPLICIT_NULL);
            asm.pload(kind, result, object, fieldOffset, true);
            resolved = asm.finishTemplate("getfield<" + kind + ">");
        }
        unresolved = null;
        return new XirPair(resolved, unresolved);
    }

    private XirPair buildPutStaticTemplate(CiKind kind, boolean genWriteBarrier) {
        final XirTemplate resolved;
        final XirTemplate unresolved;
        {
            // resolved case
            asm.restart(CiKind.Void);
            XirParameter object = asm.createInputParameter("object", CiKind.Object);
            XirParameter value = asm.createInputParameter("value", kind);
            XirParameter fieldOffset = asm.createConstantInputParameter("fieldOffset", CiKind.Int);
            asm.mark(MARK_IMPLICIT_NULL);
            asm.pstore(kind, object, fieldOffset, value, true);
            if (genWriteBarrier) {
                // TODO write barrier
                // addWriteBarrier(asm, object, value);
            }
            resolved = asm.finishTemplate("putfield<" + kind + ", " + genWriteBarrier + ">");
        }
        unresolved = null;
        return new XirPair(resolved, unresolved);
    }

    private XirPair buildInstanceof(boolean nonnull) {
        final XirTemplate resolved;
        final XirTemplate unresolved;
        {
            XirOperand result = asm.restart(CiKind.Boolean);

            XirParameter object = asm.createInputParameter("object", CiKind.Object);
            XirParameter hub = asm.createConstantInputParameter("hub", CiKind.Object);
            XirOperand objHub = asm.createTemp("objHub", CiKind.Object);

            XirLabel end = asm.createInlineLabel("end");
            XirLabel slow_path = asm.createOutOfLineLabel("slow path");

            if (!nonnull) {
                // null isn't "instanceof" anything
                asm.mov(result, asm.b(false));
                asm.jeq(end, object, asm.o(null));
            }
            asm.pload(CiKind.Object, objHub, object, asm.i(config.hubOffset), false);
            // if we get an exact match: succeed immediately
            asm.mov(result, asm.b(true));
            asm.jneq(slow_path, objHub, hub);
            asm.bindInline(end);

            // -- out of line -------------------------------------------------------
            asm.bindOutOfLine(slow_path);
            checkSubtype(result, objHub, hub);
            asm.jmp(end);

            resolved = asm.finishTemplate("instanceof-leaf<" + nonnull + ">");
        }
        {
            XirOperand result = asm.restart(CiKind.Boolean);

            XirParameter object = asm.createInputParameter("object", CiKind.Object);
            XirOperand hub = asm.createTemp("hub", CiKind.Object);
            XirOperand objHub = asm.createTemp("objHub", CiKind.Object);

            XirLabel end = asm.createInlineLabel("end");
            XirLabel slow_path = asm.createOutOfLineLabel("slow path");

            // insert the patching code for class resolving - the hub will end up in "hub"
            UnresolvedClassPatching patching = new UnresolvedClassPatching(asm, hub, config);
            patching.emitInline();

            if (!nonnull) {
                // null isn't "instanceof" anything
                asm.mov(result, asm.b(false));
                asm.jeq(end, object, asm.o(null));
            }
            asm.pload(CiKind.Object, objHub, object, asm.i(config.hubOffset), false);
            // if we get an exact match: succeed immediately
            asm.mov(result, asm.b(true));
            asm.jneq(slow_path, objHub, hub);
            asm.bindInline(end);

            // -- out of line -------------------------------------------------------
            asm.bindOutOfLine(slow_path);
            checkSubtype(result, objHub, hub);
            asm.jmp(end);

            patching.emitOutOfLine();

            unresolved = asm.finishTemplate("instanceof-leaf<" + nonnull + ">");
        }
        return new XirPair(resolved, unresolved);
    }

    private XirPair buildCheckCast() {
        final XirTemplate resolved;
        final XirTemplate unresolved;
        {
            asm.restart();

            XirParameter object = asm.createInputParameter("object", CiKind.Object);
            XirParameter hub = asm.createConstantInputParameter("hub", CiKind.Object);
            XirOperand objHub = asm.createTemp("objHub", CiKind.Object);

            XirLabel end = asm.createInlineLabel("end");
            XirLabel slow_path = asm.createOutOfLineLabel("slow path");

            // null can be cast to anything
            asm.jeq(end, object, asm.o(null));

            asm.pload(CiKind.Object, objHub, object, asm.i(config.hubOffset), false);
            // if we get an exact match: succeed immediately
            asm.jneq(slow_path, objHub, hub);
            asm.bindInline(end);

            // -- out of line -------------------------------------------------------
            asm.bindOutOfLine(slow_path);
            checkSubtype(objHub, objHub, hub);
            asm.jneq(end, objHub, asm.o(null));
            XirOperand scratch = asm.createRegister("scratch", CiKind.Object, AMD64.r10);
            asm.mov(scratch, object);
            asm.callRuntime(config.throwClassCastException, null);
            asm.shouldNotReachHere();

            resolved = asm.finishTemplate(object, "check cast");
        }
        {
            asm.restart();

            XirParameter object = asm.createInputParameter("object", CiKind.Object);
            XirOperand hub = asm.createTemp("hub", CiKind.Object);
            XirOperand objHub = asm.createTemp("objHub", CiKind.Object);

            XirLabel end = asm.createInlineLabel("end");
            XirLabel slow_path = asm.createOutOfLineLabel("slow path");

            // insert the patching code for class resolving - the hub will end up in "hub"
            UnresolvedClassPatching patching = new UnresolvedClassPatching(asm, hub, config);
            patching.emitInline();

            // null can be cast to anything
            asm.jeq(end, object, asm.o(null));

            asm.pload(CiKind.Object, objHub, object, asm.i(config.hubOffset), false);
            // if we get an exact match: succeed immediately
            asm.jneq(slow_path, objHub, hub);
            asm.bindInline(end);

            // -- out of line -------------------------------------------------------
            asm.bindOutOfLine(slow_path);
            checkSubtype(objHub, objHub, hub);
            asm.jneq(end, objHub, asm.o(null));
            XirOperand scratch = asm.createRegister("scratch", CiKind.Object, AMD64.r10);
            asm.mov(scratch, object);
            asm.callRuntime(config.throwClassCastException, null);
            asm.shouldNotReachHere();

            patching.emitOutOfLine();

            unresolved = asm.finishTemplate(object, "check cast");
        }
        return new XirPair(resolved, unresolved);
    }

    private XirTemplate buildArrayStore(CiKind kind, CiXirAssembler asm, boolean genBoundsCheck, boolean genStoreCheck, boolean genWriteBarrier, boolean withLength) {
        asm.restart(CiKind.Void);
        XirParameter array = asm.createInputParameter("array", CiKind.Object);
        XirParameter index = asm.createInputParameter("index", CiKind.Int);
        XirParameter value = asm.createInputParameter("value", kind);
        XirOperand temp = asm.createTemp("temp", CiKind.Word);
        XirOperand valueHub = null;
        XirOperand compHub = null;
        XirLabel store = asm.createInlineLabel("store");
        XirLabel failBoundsCheck = null;
        XirLabel slowStoreCheck = null;
        // if the length is known the array cannot be null
        boolean implicitNullException = !withLength;

        if (genBoundsCheck) {
            // load the array length and check the index
            failBoundsCheck = asm.createOutOfLineLabel("failBoundsCheck");
            XirOperand length;
            if (withLength) {
                length = asm.createInputParameter("length", CiKind.Int);
            } else {
                length = asm.createTemp("length", CiKind.Int);
                asm.mark(MARK_IMPLICIT_NULL);
                asm.pload(CiKind.Int, length, array, asm.i(config.arrayLengthOffset), true);
                implicitNullException = false;
            }
            asm.jugteq(failBoundsCheck, index, length);

        }
        if (genStoreCheck) {
            slowStoreCheck = asm.createOutOfLineLabel("slowStoreCheck");
            asm.jeq(store, value, asm.o(null)); // first check if value is null
            valueHub = asm.createTemp("valueHub", CiKind.Object);
            compHub = asm.createTemp("compHub", CiKind.Object);
            if (implicitNullException) {
                asm.mark(MARK_IMPLICIT_NULL);
            }
            asm.pload(CiKind.Object, compHub, array, asm.i(config.hubOffset), implicitNullException);
            asm.pload(CiKind.Object, compHub, compHub, asm.i(config.arrayClassElementOffset), false);
            asm.pload(CiKind.Object, valueHub, value, asm.i(config.hubOffset), false);
            asm.jneq(slowStoreCheck, compHub, valueHub); // then check component hub matches value hub

            implicitNullException = false;
        }
        asm.bindInline(store);
        int elemSize = target.sizeInBytes(kind);

        if (implicitNullException) {
            asm.mark(MARK_IMPLICIT_NULL);
        }
        asm.pstore(kind, array, index, value, config.getArrayOffset(kind), Scale.fromInt(elemSize), implicitNullException);
        if (genWriteBarrier) {
            // addWriteBarrier(asm, array, value);
        }

        // -- out of line -------------------------------------------------------
        if (genBoundsCheck) {
            asm.bindOutOfLine(failBoundsCheck);
            asm.callRuntime(config.throwArrayIndexException, null);
            asm.shouldNotReachHere();
        }
        if (genStoreCheck) {
            asm.bindOutOfLine(slowStoreCheck);
            checkSubtype(temp, valueHub, compHub);
            asm.jneq(store, temp, asm.w(0));
            asm.callRuntime(config.throwArrayStoreException, null);
            asm.jmp(store);
        }
        return asm.finishTemplate("arraystore<" + kind + ">");
    }

    private XirTemplate buildArrayLoad(CiKind kind, CiXirAssembler asm, boolean genBoundsCheck, boolean withLength) {
        XirOperand result = asm.restart(kind);
        XirParameter array = asm.createInputParameter("array", CiKind.Object);
        XirParameter index = asm.createInputParameter("index", CiKind.Int);
        XirLabel failBoundsCheck = null;
        // if the length is known the array cannot be null
        boolean implicitNullException = !withLength;

        if (genBoundsCheck) {
            // load the array length and check the index
            failBoundsCheck = asm.createOutOfLineLabel("failBoundsCheck");
            XirOperand length;
            if (withLength) {
                length = asm.createInputParameter("length", CiKind.Int);
            } else {
                length = asm.createTemp("length", CiKind.Int);
                asm.mark(MARK_IMPLICIT_NULL);
                asm.pload(CiKind.Int, length, array, asm.i(config.arrayLengthOffset), true);
                implicitNullException = false;
            }
            asm.jugteq(failBoundsCheck, index, length);
            implicitNullException = false;
        }
        int elemSize = target.sizeInBytes(kind);
        if (implicitNullException) {
            asm.mark(MARK_IMPLICIT_NULL);
        }
        asm.pload(kind, result, array, index, config.getArrayOffset(kind), Scale.fromInt(elemSize), implicitNullException);
        if (genBoundsCheck) {
            asm.bindOutOfLine(failBoundsCheck);
            asm.callRuntime(config.throwArrayIndexException, null);
            asm.shouldNotReachHere();
        }
        return asm.finishTemplate("arrayload<" + kind + ">");
    }

    private XirTemplate buildInvokeStatic() {
        asm.restart();
        XirParameter addr = asm.createConstantInputParameter("addr", CiKind.Word);

        XirLabel stub = asm.createOutOfLineLabel("call stub");
        asm.mark(MARK_INVOKESTATIC);

        // -- out of line -------------------------------------------------------
        asm.bindOutOfLine(stub);
        XirOperand method = asm.createRegister("method", CiKind.Word, AMD64.rbx);
        asm.mark(MARK_STATIC_CALL_STUB, XirMark.CALLSITE);
        asm.mov(method, asm.w(0l));
        XirLabel dummy = asm.createOutOfLineLabel("dummy");
        asm.jmp(dummy);
        asm.bindOutOfLine(dummy);

        return asm.finishTemplate(addr, "invokestatic");
    }

    private XirTemplate buildInvokeSpecial() {
        asm.restart();
        XirParameter receiver = asm.createInputParameter("receiver", CiKind.Object);
        XirParameter addr = asm.createConstantInputParameter("addr", CiKind.Word);
        XirOperand temp = asm.createRegister("temp", CiKind.Word, AMD64.rax);
        XirLabel stub = asm.createOutOfLineLabel("call stub");

        asm.mark(MARK_IMPLICIT_NULL);
        asm.pload(CiKind.Word, temp, receiver, true);
        asm.mark(MARK_INVOKESPECIAL);

        // -- out of line -------------------------------------------------------
        asm.bindOutOfLine(stub);
        XirOperand method = asm.createRegister("method", CiKind.Word, AMD64.rbx);
        asm.mark(MARK_STATIC_CALL_STUB, XirMark.CALLSITE);
        asm.mov(method, asm.w(0l));
        XirLabel dummy = asm.createOutOfLineLabel("dummy");
        asm.jmp(dummy);
        asm.bindOutOfLine(dummy);

        return asm.finishTemplate(addr, "invokespecial");
    }

    private XirTemplate buildInvokeInterface() {
        asm.restart();
        XirParameter receiver = asm.createInputParameter("receiver", CiKind.Object);
        XirParameter addr = asm.createConstantInputParameter("addr", CiKind.Word);
        XirOperand method = asm.createRegister("method", CiKind.Object, AMD64.rbx);
        XirOperand temp = asm.createRegister("temp", CiKind.Word, AMD64.rax);
        XirLabel stub = asm.createOutOfLineLabel("call stub");

        asm.mark(MARK_IMPLICIT_NULL);
        asm.pload(CiKind.Word, temp, receiver, true);
        asm.mark(MARK_INVOKEINTERFACE);
        asm.mov(method, asm.createConstant(CiConstant.forObject(HotSpotProxy.DUMMY_CONSTANT_OBJ)));

        // -- out of line -------------------------------------------------------
        asm.bindOutOfLine(stub);
        asm.mark(MARK_STATIC_CALL_STUB, XirMark.CALLSITE);
        asm.mov(method, asm.w(0l));
        XirLabel dummy = asm.createOutOfLineLabel("dummy");
        asm.jmp(dummy);
        asm.bindOutOfLine(dummy);

        return asm.finishTemplate(addr, "invokespecial");
    }

    private XirTemplate buildInvokeVirtual() {
        asm.restart();
        XirParameter receiver = asm.createInputParameter("receiver", CiKind.Object);
        XirParameter addr = asm.createConstantInputParameter("addr", CiKind.Word);
        XirOperand method = asm.createRegister("method", CiKind.Object, AMD64.rbx);
        XirOperand temp = asm.createRegister("temp", CiKind.Word, AMD64.rax);
        XirLabel stub = asm.createOutOfLineLabel("call stub");

        asm.mark(MARK_IMPLICIT_NULL);
        asm.pload(CiKind.Word, temp, receiver, true);
        asm.mark(MARK_INVOKEVIRTUAL);
        asm.mov(method, asm.createConstant(CiConstant.forObject(HotSpotProxy.DUMMY_CONSTANT_OBJ)));

        // -- out of line -------------------------------------------------------
        asm.bindOutOfLine(stub);
        asm.mark(MARK_STATIC_CALL_STUB, XirMark.CALLSITE);
        asm.mov(method, asm.w(0l));
        XirLabel dummy = asm.createOutOfLineLabel("dummy");
        asm.jmp(dummy);
        asm.bindOutOfLine(dummy);

        return asm.finishTemplate(addr, "invokespecial");
    }

    private XirTemplate buildNewInstance(CiXirAssembler asm, int size) {
        XirOperand result = asm.restart(CiKind.Word);
        XirOperand type = asm.createInputParameter("type", CiKind.Object);

        XirOperand thread = asm.createRegister("thread", CiKind.Word, AMD64.r15);
        XirOperand temp1 = asm.createRegister("temp1", CiKind.Word, AMD64.rcx);
        XirOperand temp2 = asm.createRegister("temp2", CiKind.Word, AMD64.rbx);
        useRegisters(asm, AMD64.rsi);
        XirLabel tlabFull = asm.createOutOfLineLabel("tlab full");
        XirLabel resume = asm.createInlineLabel("resume");

        asm.pload(CiKind.Word, result, thread, asm.i(config.threadTlabTopOffset), false);
        asm.add(temp1, result, asm.w(size));
        asm.pload(CiKind.Word, temp2, thread, asm.i(config.threadTlabEndOffset), false);

        asm.jgt(tlabFull, temp1, temp2);
        asm.pstore(CiKind.Word, thread, asm.i(config.threadTlabTopOffset), temp1, false);
        asm.bindInline(resume);

        asm.pload(CiKind.Word, temp1, type, asm.i(config.instanceHeaderPrototypeOffset), false);
        asm.pstore(CiKind.Word, result, temp1, false);
        asm.pstore(CiKind.Object, result, asm.i(config.hubOffset), type, false);

        if (size > 2 * target.wordSize) {
            asm.mov(temp1, asm.w(0));
            for (int offset = 2 * target.wordSize; offset < size; offset += target.wordSize) {
                asm.pstore(CiKind.Word, result, asm.i(offset), temp1, false);
            }
        }

        // -- out of line -------------------------------------------------------
        asm.bindOutOfLine(tlabFull);
        XirOperand arg = asm.createRegister("runtime call argument", CiKind.Object, AMD64.rdx);
        asm.mov(arg, type);
        asm.callRuntime(config.newInstanceStub, result);
        asm.jmp(resume);

        return asm.finishTemplate("new instance");
    }

    private XirTemplate buildNewInstanceUnresolved() {
        XirOperand result = asm.restart(CiKind.Word);
        XirOperand arg = asm.createRegister("runtime call argument", CiKind.Object, AMD64.rdx);

        UnresolvedClassPatching patching = new UnresolvedClassPatching(asm, arg, config);

        patching.emitInline();
        useRegisters(AMD64.rbx, AMD64.rcx, AMD64.rsi);
        asm.callRuntime(config.newInstanceStub, result);

        // -- out of line -------------------------------------------------------
        patching.emitOutOfLine();

        return asm.finishTemplate("new instance");
    }

    private XirTemplate buildNewObjectArray(boolean resolved) {
        XirOperand result = asm.restart(CiKind.Object);

        XirParameter lengthParam = asm.createInputParameter("length", CiKind.Int);

        XirOperand length = asm.createRegister("length", CiKind.Int, AMD64.rbx);
        XirOperand hub = asm.createRegister("hub", CiKind.Object, AMD64.rdx);

        UnresolvedClassPatching patching = null;
        if (resolved) {
            asm.mov(hub, asm.createConstantInputParameter("hub", CiKind.Object));
        } else {
            // insert the patching code for class resolving - the hub will end up in "hub"
            patching = new UnresolvedClassPatching(asm, hub, config);
            patching.emitInline();
        }

        asm.mov(length, lengthParam);
        useRegisters(AMD64.rsi, AMD64.rcx, AMD64.rdi);
        asm.callRuntime(config.newObjectArrayStub, result);
        if (!resolved) {
            patching.emitOutOfLine();
        }
        return asm.finishTemplate(resolved ? "newObjectArray" : "newObjectArray (unresolved)");
    }

    private XirTemplate buildMultiNewArray(int dimensions, boolean resolved) {
        XirOperand result = asm.restart(CiKind.Object);

        XirOperand hub = asm.createRegister("hub", CiKind.Object, AMD64.rax);
        XirOperand rank = asm.createRegister("rank", CiKind.Int, AMD64.rbx);
        XirOperand sizes = asm.createRegister("sizes", CiKind.Long, AMD64.rcx);
        XirOperand thread = asm.createRegister("thread", CiKind.Long, AMD64.r15);
        asm.add(sizes, thread, asm.l(config.threadMultiNewArrayStorage));
        for (int i = 0; i < dimensions; i++) {
            XirParameter length = asm.createInputParameter("length" + i, CiKind.Int);
            asm.pstore(CiKind.Int, sizes, asm.i(i * target.sizeInBytes(CiKind.Int)), length, false);
        }

        UnresolvedClassPatching patching = null;
        if (resolved) {
            asm.mov(hub, asm.createConstantInputParameter("hub", CiKind.Object));
        } else {
            // insert the patching code for class resolving - the hub will end up in "hub"
            patching = new UnresolvedClassPatching(asm, hub, config);
            patching.emitInline();
        }

        asm.mov(rank, asm.i(dimensions));
        asm.callRuntime(config.newMultiArrayStub, result);
        if (!resolved) {
            patching.emitOutOfLine();
        }
        return asm.finishTemplate(resolved ? "multiNewArray" + dimensions : "multiNewArray" + dimensions + " (unresolved)");
    }

    private XirTemplate buildNewTypeArray() {
        XirOperand result = asm.restart(CiKind.Object);

        XirParameter lengthParam = asm.createInputParameter("length", CiKind.Int);
        XirParameter hubParam = asm.createConstantInputParameter("hub", CiKind.Object);

        XirOperand length = asm.createRegister("length", CiKind.Int, AMD64.rbx);
        XirOperand hub = asm.createRegister("hub", CiKind.Object, AMD64.rdx);

        asm.mov(hub, hubParam);
        asm.mov(length, lengthParam);
        useRegisters(AMD64.rsi, AMD64.rcx, AMD64.rdi);
        asm.callRuntime(config.newTypeArrayStub, result);

        return asm.finishTemplate("newObjectArray");
    }

    private XirTemplate buildResolveClass(boolean resolved) {
        XirOperand result = asm.restart(CiKind.Word);
        if (resolved) {
            XirOperand type = asm.createConstantInputParameter("type", CiKind.Object);

            asm.mov(result, type);
        } else {
            UnresolvedClassPatching patching = new UnresolvedClassPatching(asm, result, config);
            patching.emitInline();
            // -- out of line -------------------------------------------------------
            patching.emitOutOfLine();
        }
        return asm.finishTemplate(resolved ? "resolve class" : "resolve class (unresolved)");
    }

    @Override
    public XirSnippet genArrayLength(XirSite site, XirArgument array) {
        return new XirSnippet(arrayLengthTemplate, array);
    }

    @Override
    public XirSnippet genArrayLoad(XirSite site, XirArgument array, XirArgument index, XirArgument length, CiKind elementKind, RiType elementType) {
        if (length == null) {
            return new XirSnippet(arrayLoadTemplates[elementKind.ordinal()], array, index);
        }
        return new XirSnippet(arrayLoadTemplatesWithLength[elementKind.ordinal()], array, index, length);
    }

    @Override
    public XirSnippet genArrayStore(XirSite site, XirArgument array, XirArgument index, XirArgument length, XirArgument value, CiKind elementKind, RiType elementType) {
        if (length == null) {
            return new XirSnippet(arrayStoreTemplates[elementKind.ordinal()], array, index, value);
        }
        return new XirSnippet(arrayStoreTemplatesWithLength[elementKind.ordinal()], array, index, value, length);
    }

    @Override
    public XirSnippet genCheckCast(XirSite site, XirArgument receiver, XirArgument hub, RiType type) {
        if (type.isResolved()) {
            return new XirSnippet(checkCastTemplate.resolved, receiver, hub);
        }
        return new XirSnippet(checkCastTemplate.unresolved, receiver);
    }

    @Override
    public XirSnippet genPrologue(XirSite site, RiMethod method) {
        boolean staticMethod = Modifier.isStatic(method.accessFlags());
        return new XirSnippet(staticMethod ? staticPrologueTemplate : prologueTemplate);
    }

    @Override
    public XirSnippet genEpilogue(XirSite site, RiMethod method) {
        return new XirSnippet(epilogueTemplate);
    }

    @Override
    public XirSnippet genGetField(XirSite site, XirArgument receiver, RiField field) {
        XirPair pair = getFieldTemplates[field.kind().ordinal()];
        if (field.isResolved()) {
            return new XirSnippet(pair.resolved, receiver, XirArgument.forInt(((HotSpotField) field).offset()));
        }
        return new XirSnippet(pair.unresolved, receiver);
    }

    @Override
    public XirSnippet genGetStatic(XirSite site, XirArgument staticTuple, RiField field) {
        XirPair pair = getStaticTemplates[field.kind().ordinal()];
        if (field.isResolved()) {
            return new XirSnippet(pair.resolved, staticTuple, XirArgument.forInt(((HotSpotField) field).offset()));
        }
        return new XirSnippet(pair.unresolved, staticTuple, null);
    }

    @Override
    public XirSnippet genPutField(XirSite site, XirArgument receiver, RiField field, XirArgument value) {
        XirPair pair = putFieldTemplates[field.kind().ordinal()];
        if (field.isResolved()) {
            return new XirSnippet(pair.resolved, receiver, value, XirArgument.forInt(((HotSpotField) field).offset()));
        }
        return new XirSnippet(pair.unresolved, receiver, value);
    }

    @Override
    public XirSnippet genPutStatic(XirSite site, XirArgument staticTuple, RiField field, XirArgument value) {
        XirPair pair = putStaticTemplates[field.kind().ordinal()];
        if (field.isResolved()) {
            return new XirSnippet(pair.resolved, staticTuple, value, XirArgument.forInt(((HotSpotField) field).offset()));
        }
        return new XirSnippet(pair.unresolved, staticTuple, value);
    }

    @Override
    public XirSnippet genInstanceOf(XirSite site, XirArgument receiver, XirArgument hub, RiType type) {
        if (type.isResolved()) {
            return new XirSnippet(instanceofTemplate.resolved, receiver, hub);
        }
        return new XirSnippet(instanceofTemplate.unresolved, receiver);
    }

    @Override
    public XirSnippet genIntrinsic(XirSite site, XirArgument[] arguments, RiMethod method) {
        return null;
    }

    @Override
    public XirSnippet genInvokeInterface(XirSite site, XirArgument receiver, RiMethod method) {
        return new XirSnippet(invokeInterfaceTemplate, receiver, XirArgument.forWord(0));
    }

    @Override
    public XirSnippet genInvokeSpecial(XirSite site, XirArgument receiver, RiMethod method) {
        return new XirSnippet(invokeSpecialTemplate, receiver, XirArgument.forWord(0));
    }

    @Override
    public XirSnippet genInvokeStatic(XirSite site, RiMethod method) {
        return new XirSnippet(invokeStaticTemplate, XirArgument.forWord(0));
    }

    @Override
    public XirSnippet genInvokeVirtual(XirSite site, XirArgument receiver, RiMethod method) {
        return new XirSnippet(invokeVirtualTemplate, receiver, XirArgument.forWord(0));
    }

    @Override
    public XirSnippet genMonitorEnter(XirSite site, XirArgument receiver) {
        return new XirSnippet(emptyTemplates[CiKind.Void.ordinal()]);
    }

    @Override
    public XirSnippet genMonitorExit(XirSite site, XirArgument receiver) {
        return new XirSnippet(emptyTemplates[CiKind.Void.ordinal()]);
    }

    @Override
    public XirSnippet genNewArray(XirSite site, XirArgument length, CiKind elementKind, RiType componentType, RiType arrayType) {
        if (elementKind == CiKind.Object) {
            if (arrayType instanceof HotSpotTypeResolved) {
                return new XirSnippet(newObjectArrayTemplate.resolved, length, XirArgument.forObject(arrayType));
            }
            return new XirSnippet(newObjectArrayTemplate.unresolved, length);
        }
        assert arrayType == null;
        arrayType = Compiler.getVMEntries().getPrimitiveArrayType(elementKind);
        return new XirSnippet(newTypeArrayTemplate, length, XirArgument.forObject(arrayType));
    }

    @Override
    public XirSnippet genNewInstance(XirSite site, RiType type) {
        if (type instanceof HotSpotTypeResolved) {
            int instanceSize = ((HotSpotTypeResolved) type).instanceSize();
            return new XirSnippet(newInstanceTemplates.get(instanceSize), XirArgument.forObject(type));
        }
        return new XirSnippet(newInstanceUnresolvedTemplate);
    }

    @Override
    public XirSnippet genNewMultiArray(XirSite site, XirArgument[] lengths, RiType type) {
        if (type instanceof HotSpotTypeResolved) {
            XirArgument[] params = Arrays.copyOf(lengths, lengths.length + 1);
            params[lengths.length] = XirArgument.forObject(type);
            return new XirSnippet(multiNewArrayTemplate.get(lengths.length).resolved, params);
        }
        return new XirSnippet(multiNewArrayTemplate.get(lengths.length).unresolved, lengths);
    }

    @Override
    public XirSnippet genResolveClass(XirSite site, RiType type, Representation representation) {
        assert representation == Representation.ObjectHub;
        if (type instanceof HotSpotTypeResolved) {
            return new XirSnippet(resolveClassTemplate.resolved, XirArgument.forObject(type));
        }
        return new XirSnippet(resolveClassTemplate.unresolved);
    }

    @Override
    public XirSnippet genSafepoint(XirSite site) {
        return new XirSnippet(safepointTemplate);
    }

    @Override
    public XirSnippet genExceptionObject(XirSite site) {
        return new XirSnippet(exceptionObjectTemplate);
    }

    private static class UnresolvedClassPatching {

        private final XirLabel patchSite;
        private final XirLabel replacement;
        private final XirLabel patchStub;
        private final CiXirAssembler asm;
        private final HotSpotVMConfig config;
        private final XirOperand arg;
        private State state;

        private enum State {
            New, Inline, Finished
        }

        public UnresolvedClassPatching(CiXirAssembler asm, XirOperand arg, HotSpotVMConfig config) {
            this.asm = asm;
            this.arg = arg;
            this.config = config;
            patchSite = asm.createInlineLabel("patch site");
            replacement = asm.createOutOfLineLabel("replacement");
            patchStub = asm.createOutOfLineLabel("patch stub");

            state = State.New;
        }

        public void emitInline() {
            assert state == State.New;

            asm.bindInline(patchSite);
            asm.mark(MARK_DUMMY_OOP_RELOCATION);
            asm.jmp(patchStub);

            // TODO: make this more generic & safe - this is needed to create space for patching
            asm.nop(5);

            state = State.Inline;
        }

        public void emitOutOfLine() {
            assert state == State.Inline;

            asm.bindOutOfLine(replacement);
            XirMark begin = asm.mark(null);
            asm.mov(arg, asm.createConstant(CiConstant.forObject(null)));
            XirMark end = asm.mark(null);
            // make this piece of data look like an instruction
            asm.rawBytes(new byte[] { (byte) 0xb8, 0, 0, 0x05, 0});
            asm.mark(MARK_KLASS_PATCHING, begin, end);
            asm.bindOutOfLine(patchStub);
            asm.callRuntime(config.loadKlassStub, null);
            asm.jmp(patchSite);

            state = State.Finished;
        }
    }

    private void checkSubtype(XirOperand result, XirOperand objHub, XirOperand hub) {
        asm.push(objHub);
        asm.push(hub);
        asm.callRuntime(config.instanceofStub, null);
        asm.pop(result);
        asm.pop(result);
    }

    private void useRegisters(CiXirAssembler asm, CiRegister... registers) {
        if (registers != null) {
            for (CiRegister register : registers) {
                asm.createRegister("reg", CiKind.Illegal, register);
            }
        }
    }

    private void useRegisters(CiRegister... registers) {
        useRegisters(asm, registers);
    }

    private abstract class OndemandTemplates<T> {

        private ConcurrentHashMap<Integer, T> templates = new ConcurrentHashMap<Integer, T>();

        protected abstract T create(CiXirAssembler asm, int index);

        public T get(int index) {
            T template = templates.get(index);
            if (template == null) {
                template = create(asm.copy(), index);
                templates.put(index, template);
            }
            return template;
        }
    }
}