All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.objectfile.elf.dwarf.DwarfInfoSectionImpl Maven / Gradle / Ivy

There is a newer version: 24.1.1
Show newest version
/*
 * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2020, 2020, Red Hat Inc. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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.oracle.objectfile.elf.dwarf;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;

import com.oracle.objectfile.elf.dwarf.constants.DwarfAccess;
import com.oracle.objectfile.elf.dwarf.constants.DwarfEncoding;
import com.oracle.objectfile.elf.dwarf.constants.DwarfExpressionOpcode;
import com.oracle.objectfile.elf.dwarf.constants.DwarfFlag;
import com.oracle.objectfile.elf.dwarf.constants.DwarfInline;
import com.oracle.objectfile.elf.dwarf.constants.DwarfLanguage;
import com.oracle.objectfile.elf.dwarf.constants.DwarfSectionName;
import com.oracle.objectfile.elf.dwarf.constants.DwarfVersion;
import org.graalvm.collections.EconomicSet;
import jdk.graal.compiler.debug.DebugContext;

import com.oracle.objectfile.debugentry.ArrayTypeEntry;
import com.oracle.objectfile.debugentry.ClassEntry;
import com.oracle.objectfile.debugentry.CompiledMethodEntry;
import com.oracle.objectfile.debugentry.FieldEntry;
import com.oracle.objectfile.debugentry.FileEntry;
import com.oracle.objectfile.debugentry.ForeignTypeEntry;
import com.oracle.objectfile.debugentry.HeaderTypeEntry;
import com.oracle.objectfile.debugentry.InterfaceClassEntry;
import com.oracle.objectfile.debugentry.MethodEntry;
import com.oracle.objectfile.debugentry.PrimitiveTypeEntry;
import com.oracle.objectfile.debugentry.StructureTypeEntry;
import com.oracle.objectfile.debugentry.TypeEntry;
import com.oracle.objectfile.debugentry.range.Range;
import com.oracle.objectfile.debugentry.range.SubRange;
import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo;
import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalValueInfo;
import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo;
import com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.AbbrevCode;

import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.PrimitiveConstant;

/**
 * Section generator for debug_info section.
 */
public class DwarfInfoSectionImpl extends DwarfSectionImpl {
    /**
     * The name of a special DWARF struct type used to model an object header.
     */
    private static final String OBJECT_HEADER_STRUCT_NAME = "_objhdr";

    /**
     * An info header section always contains a fixed number of bytes.
     */
    private static final int DIE_HEADER_SIZE = 11;
    /**
     * Normally the offset of DWARF type declarations are tracked using the type/class entry
     * properties but that means they are only available to be read during the second pass when
     * filling in type cross-references. However, we need to use the offset of the void type during
     * the first pass as the target of later-generated foreign pointer types. So, this field saves
     * it up front.
     */
    private int voidOffset;
    private int cuStart;

    public DwarfInfoSectionImpl(DwarfDebugInfo dwarfSections) {
        // debug_info section depends on loc section
        super(dwarfSections, DwarfSectionName.DW_INFO_SECTION, DwarfSectionName.DW_LOC_SECTION);
        // initialize to an invalid value
        voidOffset = -1;
        // initialize CU start to an invalid value
        cuStart = -1;
    }

    @Override
    public void createContent() {
        assert !contentByteArrayCreated();

        byte[] buffer = null;
        int len = generateContent(null, buffer);

        buffer = new byte[len];
        super.setContent(buffer);
    }

    @Override
    public void writeContent(DebugContext context) {
        assert contentByteArrayCreated();

        byte[] buffer = getContent();
        int size = buffer.length;
        int pos = 0;

        enableLog(context, pos);
        log(context, "  [0x%08x] DEBUG_INFO", pos);
        log(context, "  [0x%08x] size = 0x%08x", pos, size);

        pos = generateContent(context, buffer);
        assert pos == size;
    }

    DwarfEncoding computeEncoding(int flags, int bitCount) {
        assert bitCount > 0;
        if ((flags & DebugPrimitiveTypeInfo.FLAG_NUMERIC) != 0) {
            if (((flags & DebugPrimitiveTypeInfo.FLAG_INTEGRAL) != 0)) {
                if ((flags & DebugPrimitiveTypeInfo.FLAG_SIGNED) != 0) {
                    switch (bitCount) {
                        case 8:
                            return DwarfEncoding.DW_ATE_signed_char;
                        default:
                            assert bitCount == 16 || bitCount == 32 || bitCount == 64;
                            return DwarfEncoding.DW_ATE_signed;
                    }
                } else {
                    assert bitCount == 16;
                    return DwarfEncoding.DW_ATE_unsigned;
                }
            } else {
                assert bitCount == 32 || bitCount == 64;
                return DwarfEncoding.DW_ATE_float;
            }
        } else {
            assert bitCount == 1;
            return DwarfEncoding.DW_ATE_boolean;
        }
    }

    public int generateContent(DebugContext context, byte[] buffer) {
        int pos = 0;
        /* Write CU for primitive types and header struct. */

        pos = writeBuiltInTypes(context, buffer, pos);

        /*
         * Write CUs for all instance classes, which includes interfaces and enums. That also
         * incorporates interfaces that model foreign types.
         */

        pos = writeInstanceClasses(context, buffer, pos);

        /* Write CUs for array types. */

        pos = writeArrays(context, buffer, pos);

        return pos;
    }

    private int writeBuiltInTypes(DebugContext context, byte[] buffer, int p) {
        int pos = p;
        // Write the single Java builtin unit header
        int lengthPos = pos;
        log(context, "  [0x%08x] <0> Java Builtin Compile Unit", pos);
        cuStart = p;
        pos = writeCUHeader(buffer, pos);
        assert pos == lengthPos + DIE_HEADER_SIZE;
        AbbrevCode abbrevCode = AbbrevCode.BUILTIN_UNIT;
        log(context, "  [0x%08x] <0> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     language  %s", pos, "DW_LANG_Java");
        pos = writeAttrLanguage(DwarfDebugInfo.LANG_ENCODING, buffer, pos);
        log(context, "  [0x%08x]     use_UTF8", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        String name = uniqueDebugString("JAVA");
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        String compilationDirectory = dwarfSections.getCachePath();
        log(context, "  [0x%08x]     comp_dir  0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory);
        pos = writeStrSectionOffset(compilationDirectory, buffer, pos);
        /* Write child entries for primitive Java types. */

        pos = primitiveTypeStream().reduce(pos,
                        (pos1, primitiveTypeEntry) -> {
                            if (primitiveTypeEntry.getBitCount() > 0) {
                                return writePrimitiveType(context, primitiveTypeEntry, buffer, pos1);
                            } else {
                                return writeVoidType(context, primitiveTypeEntry, buffer, pos1);
                            }
                        },
                        (oldpos, newpos) -> newpos);

        /* Write child entry for object/array header struct. */

        pos = writeHeaderType(context, headerType(), buffer, pos);

        /* write class constants for primitive type classes */

        pos = primitiveTypeStream().reduce(pos,
                        (pos1, primitiveTypeEntry) -> writeClassConstantDeclaration(context, primitiveTypeEntry, buffer, pos1),
                        (oldpos, newpos) -> newpos);

        /*
         * Write a terminating null attribute.
         */
        pos = writeAttrNull(buffer, pos);

        /* Fix up the CU length. */

        patchLength(lengthPos, buffer, pos);
        return pos;
    }

    public int writePrimitiveType(DebugContext context, PrimitiveTypeEntry primitiveTypeEntry, byte[] buffer, int p) {
        assert primitiveTypeEntry.getBitCount() > 0;
        int pos = p;
        log(context, "  [0x%08x] primitive type %s", pos, primitiveTypeEntry.getTypeName());
        /* Record the location of this type entry. */
        setTypeIndex(primitiveTypeEntry, pos);
        /*
         * primitive fields never need an indirection so use the same index for places where we
         * might want an indirect type
         */
        setIndirectTypeIndex(primitiveTypeEntry, pos);
        AbbrevCode abbrevCode = AbbrevCode.PRIMITIVE_TYPE;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        byte byteSize = (byte) primitiveTypeEntry.getSize();
        log(context, "  [0x%08x]     byte_size  %d", pos, byteSize);
        pos = writeAttrData1(byteSize, buffer, pos);
        byte bitCount = (byte) primitiveTypeEntry.getBitCount();
        log(context, "  [0x%08x]     bitCount  %d", pos, bitCount);
        pos = writeAttrData1(bitCount, buffer, pos);
        DwarfEncoding encoding = computeEncoding(primitiveTypeEntry.getFlags(), bitCount);
        log(context, "  [0x%08x]     encoding  0x%x", pos, encoding.value());
        pos = writeAttrEncoding(encoding, buffer, pos);
        String name = primitiveTypeEntry.getTypeName();
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        return writeStrSectionOffset(name, buffer, pos);
    }

    public int writeVoidType(DebugContext context, PrimitiveTypeEntry primitiveTypeEntry, byte[] buffer, int p) {
        assert primitiveTypeEntry.getBitCount() == 0;
        int pos = p;
        log(context, "  [0x%08x] primitive type void", pos);
        /* Record the location of this type entry. */
        setTypeIndex(primitiveTypeEntry, pos);
        /*
         * Type void never needs an indirection so use the same index for places where we might want
         * an indirect type.
         */
        setIndirectTypeIndex(primitiveTypeEntry, pos);
        // specially record void type offset for immediate use during first pass of info generation
        // we need to use it as the base layout for foreign types
        assert voidOffset == -1 || voidOffset == pos;
        voidOffset = pos;
        AbbrevCode abbrevCode = AbbrevCode.VOID_TYPE;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        String name = primitiveTypeEntry.getTypeName();
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        return writeStrSectionOffset(name, buffer, pos);
    }

    public int writeHeaderType(DebugContext context, HeaderTypeEntry headerTypeEntry, byte[] buffer, int p) {
        int pos = p;
        String name = headerTypeEntry.getTypeName();
        byte size = (byte) headerTypeEntry.getSize();
        log(context, "  [0x%08x] header type %s", pos, name);
        /* Record the location of this type entry. */
        setTypeIndex(headerTypeEntry, pos);
        /*
         * Header records don't need an indirection so use the same index for places where we might
         * want an indirect type.
         */
        setIndirectTypeIndex(headerTypeEntry, pos);
        AbbrevCode abbrevCode = AbbrevCode.OBJECT_HEADER;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        log(context, "  [0x%08x]     byte_size  0x%x", pos, size);
        pos = writeAttrData1(size, buffer, pos);
        pos = writeStructFields(context, headerTypeEntry.fields(), buffer, pos);
        /*
         * Write a terminating null attribute.
         */
        return writeAttrNull(buffer, pos);
    }

    private int writeStructFields(DebugContext context, Stream fields, byte[] buffer, int p) {
        Cursor cursor = new Cursor(p);
        fields.forEach(fieldEntry -> {
            cursor.set(writeStructField(context, fieldEntry, buffer, cursor.get()));
        });
        return cursor.get();
    }

    private int writeStructField(DebugContext context, FieldEntry fieldEntry, byte[] buffer, int p) {
        int pos = p;
        String fieldName = fieldEntry.fieldName();
        TypeEntry valueType = fieldEntry.getValueType();
        int valueTypeIdx;
        if (fieldEntry.isEmbedded()) {
            // the field type must be a foreign type
            ForeignTypeEntry foreignValueType = (ForeignTypeEntry) valueType;
            /* use the indirect layout type for the field */
            /* handle special case when the field is an array */
            int fieldSize = fieldEntry.getSize();
            int valueSize = foreignValueType.getSize();
            if (fieldEntry.getSize() != foreignValueType.getSize()) {
                assert (fieldSize % valueSize == 0) : "embedded field size is not a multiple of value type size!";
                // declare a local array of the embedded type and use it as the value type
                valueTypeIdx = pos;
                pos = writeEmbeddedArrayDataType(context, foreignValueType, valueSize, fieldSize / valueSize, buffer, pos);
            } else {
                if (foreignValueType.isPointer()) {
                    TypeEntry pointerTo = foreignValueType.getPointerTo();
                    assert pointerTo != null : "ADDRESS field pointer type must have a known target type";
                    // type the array using the referent of the pointer type
                    //
                    // n.b it is critical for correctness to use the index of the referent rather
                    // than the layout type of the referring type even though the latter will
                    // (eventually) be set to the same value. the type index of the referent is
                    // guaranteed to be set on the first sizing pass before it is consumed here
                    // on the second writing pass.
                    // However, if this embedded struct field definition precedes the definition
                    // of the referring type and the latter precedes the definition of the
                    // referent type then the layout index of the referring type may still be unset
                    // at this point.
                    valueTypeIdx = getTypeIndex(pointerTo);
                } else {
                    valueTypeIdx = getIndirectLayoutIndex(foreignValueType);
                }
            }
        } else {
            /* use the indirect type for the field so pointers get translated */
            valueTypeIdx = getIndirectTypeIndex(valueType);
        }
        log(context, "  [0x%08x] struct field", pos);
        AbbrevCode abbrevCode = AbbrevCode.HEADER_FIELD;
        log(context, "  [0x%08x] <2> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(fieldName), fieldName);
        pos = writeStrSectionOffset(fieldName, buffer, pos);
        log(context, "  [0x%08x]     type 0x%x (%s)", pos, valueTypeIdx, valueType.getTypeName());
        pos = writeInfoSectionOffset(valueTypeIdx, buffer, pos);
        short offset = (short) fieldEntry.getOffset();
        int size = fieldEntry.getSize();
        log(context, "  [0x%08x]     offset 0x%x (size 0x%x)", pos, offset, size);
        pos = writeAttrData2(offset, buffer, pos);
        int modifiers = fieldEntry.getModifiers();
        log(context, "  [0x%08x]     modifiers %s", pos, fieldEntry.getModifiersString());
        return writeAttrAccessibility(modifiers, buffer, pos);
    }

    private int writeInstanceClasses(DebugContext context, byte[] buffer, int pos) {
        log(context, "  [0x%08x] instance classes", pos);
        Cursor cursor = new Cursor(pos);
        instanceClassStream().forEach(classEntry -> {
            // save current CU start so we can write Ref4 attributes as CU offsets
            cuStart = cursor.get();
            setCUIndex(classEntry, cuStart);
            cursor.set(writeInstanceClassInfo(context, classEntry, buffer, cursor.get()));
        });
        return cursor.get();
    }

    private int writeInstanceClassInfo(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
        int pos = p;
        // Write a Java class unit header
        int lengthPos = pos;
        log(context, "  [0x%08x] Instance class unit", pos);
        pos = writeCUHeader(buffer, pos);
        assert pos == lengthPos + DIE_HEADER_SIZE;
        AbbrevCode abbrevCode = (classEntry.hasCompiledEntries() ? AbbrevCode.CLASS_UNIT_2 : AbbrevCode.CLASS_UNIT_1);
        log(context, "  [0x%08x] <0> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     language  %s", pos, "DW_LANG_Java");
        pos = writeAttrLanguage(DwarfDebugInfo.LANG_ENCODING, buffer, pos);
        log(context, "  [0x%08x]     use_UTF8", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        String name = classEntry.getFullFileName();
        if (name == null) {
            name = classEntry.getTypeName().replace('.', '/') + ".java";
        }
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(uniqueDebugString(name), buffer, pos);
        String compilationDirectory = dwarfSections.getCachePath();
        log(context, "  [0x%08x]     comp_dir  0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory);
        pos = writeStrSectionOffset(compilationDirectory, buffer, pos);
        if (abbrevCode == AbbrevCode.CLASS_UNIT_2) {
            int codeRangesIndex = getCodeRangesIndex(classEntry);
            log(context, "  [0x%08x]     ranges  0x%x", pos, codeRangesIndex);
            pos = writeRangesSectionOffset(codeRangesIndex, buffer, pos);
            // write low_pc as well as ranges so that lcoation lists can default the base address
            int lo = classEntry.lowpc();
            log(context, "  [0x%08x]     low_pc  0x%x", pos, codeRangesIndex);
            pos = writeAttrAddress(lo, buffer, pos);
            int lineIndex = getLineIndex(classEntry);
            log(context, "  [0x%08x]     stmt_list  0x%x", pos, lineIndex);
            pos = writeLineSectionOffset(lineIndex, buffer, pos);
        }
        /* if the class has a loader then embed the children in a namespace */
        String loaderId = classEntry.getLoaderId();
        if (!loaderId.isEmpty()) {
            pos = writeNameSpace(context, loaderId, buffer, pos);
        }

        /* Now write the child DIEs starting with the layout and pointer type. */

        if (classEntry.isInterface()) {
            InterfaceClassEntry interfaceClassEntry = (InterfaceClassEntry) classEntry;
            pos = writeInterfaceLayout(context, interfaceClassEntry, buffer, pos);
            pos = writeInterfaceType(context, interfaceClassEntry, buffer, pos);
        } else if (classEntry.isForeign()) {
            ForeignTypeEntry foreignTypeEntry = (ForeignTypeEntry) classEntry;
            pos = writeForeignLayout(context, foreignTypeEntry, buffer, pos);
            pos = writeForeignType(context, foreignTypeEntry, buffer, pos);
        } else {
            pos = writeClassLayout(context, classEntry, buffer, pos);
            pos = writeClassType(context, classEntry, buffer, pos);
        }

        /* Write a declaration for the special Class object pseudo-static field */
        pos = writeClassConstantDeclaration(context, classEntry, buffer, pos);

        /* if we opened a namespace then terminate its children */

        /* Write all compiled code locations */

        pos = writeMethodLocations(context, classEntry, buffer, pos);

        /* Write abstract inline methods. */

        pos = writeAbstractInlineMethods(context, classEntry, buffer, pos);

        /* Write all static field definitions */

        pos = writeStaticFieldLocations(context, classEntry, buffer, pos);

        if (!loaderId.isEmpty()) {
            pos = writeAttrNull(buffer, pos);
        }

        /*
         * Write a terminating null attribute.
         */
        pos = writeAttrNull(buffer, pos);

        /* Fix up the CU length. */

        patchLength(lengthPos, buffer, pos);
        return pos;
    }

    private int writeNameSpace(DebugContext context, String id, byte[] buffer, int p) {
        int pos = p;
        String name = uniqueDebugString(id);
        assert !id.isEmpty();
        log(context, "  [0x%08x] namespace %s", pos, name);
        AbbrevCode abbrevCode = AbbrevCode.NAMESPACE;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        return pos;
    }

    private int writeClassConstantDeclaration(DebugContext context, TypeEntry typeEntry, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] class constant", pos);
        long offset = typeEntry.getClassOffset();
        if (offset < 0) {
            return pos;
        }
        // Write a special static field declaration for the class object
        // we use the abbrev code for a static field with no file or line location
        AbbrevCode abbrevCode = AbbrevCode.CLASS_CONSTANT;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);

        String name = uniqueDebugString(typeEntry.getTypeName() + ".class");
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        /*
         * This is a direct reference to the object rather than a compressed oop reference. So, we
         * need to use the direct layout type for hub class to type it.
         */
        ClassEntry valueType = dwarfSections.getHubClassEntry();
        int typeIdx = (valueType == null ? -1 : getLayoutIndex(valueType));
        log(context, "  [0x%08x]     type  0x%x ()", pos, typeIdx);
        pos = writeInfoSectionOffset(typeIdx, buffer, pos);
        log(context, "  [0x%08x]     accessibility public static final", pos);
        pos = writeAttrAccessibility(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, buffer, pos);
        log(context, "  [0x%08x]     external(true)", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        log(context, "  [0x%08x]     definition(true)", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        /*
         * We need to force encoding of this location as a heap base relative relocatable address
         * rather than an offset from the heapbase register.
         */
        log(context, "  [0x%08x]     location  heapbase + 0x%x (class constant)", pos, offset);
        pos = writeHeapLocationExprLoc(offset, false, buffer, pos);
        return pos;
    }

    private int writeClassLayout(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
        int pos = p;
        int layoutIndex = pos;
        setLayoutIndex(classEntry, layoutIndex);
        log(context, "  [0x%08x] class layout", pos);
        AbbrevCode abbrevCode = AbbrevCode.CLASS_LAYOUT_1;
        /*
         * when we don't have a separate indirect type then hub layouts need an extra data_location
         * attribute
         */
        if (!dwarfSections.useHeapBase() && dwarfSections.isHubClassEntry(classEntry)) {
            abbrevCode = AbbrevCode.CLASS_LAYOUT_2;
        }
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        String name = classEntry.getTypeName();
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        int size = classEntry.getSize();
        log(context, "  [0x%08x]     byte_size 0x%x", pos, size);
        pos = writeAttrData2((short) size, buffer, pos);
        int fileIdx = classEntry.getFileIdx();
        log(context, "  [0x%08x]     file  0x%x (%s)", pos, fileIdx, classEntry.getFileName());
        pos = writeAttrData2((short) fileIdx, buffer, pos);
        if (abbrevCode == AbbrevCode.CLASS_LAYOUT_2) {
            /* Write a data location expression to mask and/or rebase oop pointers. */
            log(context, "  [0x%08x]     data_location", pos);
            pos = writeIndirectOopConversionExpression(true, buffer, pos);
        }
        int superTypeOffset;
        String superName;
        ClassEntry superClassEntry = classEntry.getSuperClass();
        if (superClassEntry != null) {
            /* Inherit layout from super class. */
            superName = superClassEntry.getTypeName();
            superTypeOffset = getLayoutIndex(superClassEntry);
        } else {
            /* Inherit layout from object header. */
            superName = OBJECT_HEADER_STRUCT_NAME;
            TypeEntry headerType = headerType();
            superTypeOffset = getTypeIndex(headerType);
        }
        /* Now write the child fields. */
        pos = writeSuperReference(context, superTypeOffset, superName, buffer, pos);
        pos = writeFields(context, classEntry, buffer, pos);
        pos = writeMethodDeclarations(context, classEntry, buffer, pos);
        /*
         * Write a terminating null attribute.
         */
        pos = writeAttrNull(buffer, pos);

        if (dwarfSections.useHeapBase()) {
            /*
             * Write a wrapper type with a data_location attribute that can act as a target for an
             * indirect pointer.
             */
            setIndirectLayoutIndex(classEntry, pos);
            log(context, "  [0x%08x] indirect class layout", pos);
            abbrevCode = AbbrevCode.INDIRECT_LAYOUT;
            log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
            pos = writeAbbrevCode(abbrevCode, buffer, pos);
            String indirectName = uniqueDebugString(DwarfDebugInfo.INDIRECT_PREFIX + classEntry.getTypeName());
            log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(indirectName), name);
            pos = writeStrSectionOffset(indirectName, buffer, pos);
            log(context, "  [0x%08x]     byte_size 0x%x", pos, size);
            pos = writeAttrData2((short) size, buffer, pos);
            /* Write a data location expression to mask and/or rebase oop pointers. */
            log(context, "  [0x%08x]     data_location", pos);
            pos = writeIndirectOopConversionExpression(dwarfSections.isHubClassEntry(classEntry), buffer, pos);
            superTypeOffset = layoutIndex;
            /* Now write the child field. */
            pos = writeSuperReference(context, superTypeOffset, superName, buffer, pos);
            /*
             * Write a terminating null attribute.
             */
            pos = writeAttrNull(buffer, pos);
        } else {
            log(context, "  [0x%08x] setIndirectLayoutIndex %s 0x%x", pos, classEntry.getTypeName(), pos);
            setIndirectLayoutIndex(classEntry, layoutIndex);
        }

        return pos;
    }

    private int writeSuperReference(DebugContext context, int superTypeOffset, String superName, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] super reference", pos);
        AbbrevCode abbrevCode = AbbrevCode.SUPER_REFERENCE;
        log(context, "  [0x%08x] <2> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     type 0x%x (%s)", pos, superTypeOffset, superName);
        pos = writeInfoSectionOffset(superTypeOffset, buffer, pos);
        /* Parent layout is embedded at start of object. */
        log(context, "  [0x%08x]     data_member_location (super) 0x%x", pos, 0);
        pos = writeAttrData1((byte) 0, buffer, pos);
        log(context, "  [0x%08x]     modifiers public", pos);
        int modifiers = Modifier.PUBLIC;
        pos = writeAttrAccessibility(modifiers, buffer, pos);
        return pos;
    }

    private int writeFields(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
        return classEntry.fields().filter(DwarfInfoSectionImpl::isManifestedField).reduce(p,
                        (pos, fieldEntry) -> writeField(context, classEntry, fieldEntry, buffer, pos),
                        (oldPos, newPos) -> newPos);
    }

    private static boolean isManifestedField(FieldEntry fieldEntry) {
        return fieldEntry.getOffset() >= 0;
    }

    private int writeField(DebugContext context, StructureTypeEntry entry, FieldEntry fieldEntry, byte[] buffer, int p) {
        int pos = p;
        int modifiers = fieldEntry.getModifiers();
        boolean hasFile = fieldEntry.getFileName().length() > 0;
        log(context, "  [0x%08x] field definition", pos);
        AbbrevCode abbrevCode;
        boolean isStatic = Modifier.isStatic(modifiers);
        if (!isStatic) {
            if (!hasFile) {
                abbrevCode = AbbrevCode.FIELD_DECLARATION_1;
            } else {
                abbrevCode = AbbrevCode.FIELD_DECLARATION_2;
            }
        } else {
            if (!hasFile) {
                abbrevCode = AbbrevCode.FIELD_DECLARATION_3;
            } else {
                abbrevCode = AbbrevCode.FIELD_DECLARATION_4;
            }
            /* Record the position of the declaration to use when we write the definition. */
            setFieldDeclarationIndex(entry, fieldEntry.fieldName(), pos);
        }
        log(context, "  [0x%08x] <2> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);

        String name = fieldEntry.fieldName();
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        /* We may not have a file and line for a field. */
        if (hasFile) {
            assert entry instanceof ClassEntry;
            int fileIdx = fieldEntry.getFileIdx();
            assert fileIdx > 0;
            log(context, "  [0x%08x]     filename  0x%x (%s)", pos, fileIdx, fieldEntry.getFileName());
            pos = writeAttrData2((short) fileIdx, buffer, pos);
            /* At present we definitely don't have line numbers. */
        }
        TypeEntry valueType = fieldEntry.getValueType();
        /* use the indirect type for the field so pointers get translated if needed */
        int typeIdx = getIndirectTypeIndex(valueType);
        log(context, "  [0x%08x]     type  0x%x (%s)", pos, typeIdx, valueType.getTypeName());
        pos = writeInfoSectionOffset(typeIdx, buffer, pos);
        if (!isStatic) {
            int memberOffset = fieldEntry.getOffset();
            log(context, "  [0x%08x]     member offset 0x%x", pos, memberOffset);
            pos = writeAttrData2((short) memberOffset, buffer, pos);
        }
        log(context, "  [0x%08x]     accessibility %s", pos, fieldEntry.getModifiersString());
        pos = writeAttrAccessibility(fieldEntry.getModifiers(), buffer, pos);
        /* Static fields are only declared here and are external. */
        if (isStatic) {
            log(context, "  [0x%08x]     external(true)", pos);
            pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
            log(context, "  [0x%08x]     definition(true)", pos);
            pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        }
        return pos;
    }

    private int writeMethodDeclarations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
        int pos = p;
        for (MethodEntry method : classEntry.getMethods()) {
            if (method.isInRange() || method.isInlined()) {
                /*
                 * Declare all methods whether or not they have been compiled or inlined.
                 */
                pos = writeMethodDeclaration(context, classEntry, method, buffer, pos);
            }
        }

        return pos;
    }

    private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, MethodEntry method, byte[] buffer, int p) {
        int pos = p;
        String methodKey = method.getSymbolName();
        String linkageName = uniqueDebugString(methodKey);
        setMethodDeclarationIndex(method, pos);
        int modifiers = method.getModifiers();
        boolean isStatic = Modifier.isStatic(modifiers);
        log(context, "  [0x%08x] method declaration %s::%s", pos, classEntry.getTypeName(), method.methodName());
        AbbrevCode abbrevCode = (isStatic ? AbbrevCode.METHOD_DECLARATION_STATIC : AbbrevCode.METHOD_DECLARATION);
        log(context, "  [0x%08x] <2> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     external  true", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        String name = uniqueDebugString(method.methodName());
        log(context, "  [0x%08x]     name 0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        FileEntry fileEntry = method.getFileEntry();
        if (fileEntry == null) {
            fileEntry = classEntry.getFileEntry();
        }
        assert fileEntry != null;
        int fileIdx = classEntry.getFileIdx(fileEntry);
        log(context, "  [0x%08x]     file 0x%x (%s)", pos, fileIdx, fileEntry.getFullName());
        pos = writeAttrData2((short) fileIdx, buffer, pos);
        int line = method.getLine();
        log(context, "  [0x%08x]     line 0x%x", pos, line);
        pos = writeAttrData2((short) line, buffer, pos);
        log(context, "  [0x%08x]     linkage_name %s", pos, linkageName);
        pos = writeStrSectionOffset(linkageName, buffer, pos);
        TypeEntry returnType = method.getValueType();
        int retTypeIdx = getTypeIndex(returnType);
        log(context, "  [0x%08x]     type 0x%x (%s)", pos, retTypeIdx, returnType.getTypeName());
        pos = writeInfoSectionOffset(retTypeIdx, buffer, pos);
        log(context, "  [0x%08x]     artificial %s", pos, method.isDeopt() ? "true" : "false");
        pos = writeFlag((method.isDeopt() ? DwarfFlag.DW_FLAG_true : DwarfFlag.DW_FLAG_false), buffer, pos);
        log(context, "  [0x%08x]     accessibility %s", pos, "public");
        pos = writeAttrAccessibility(modifiers, buffer, pos);
        log(context, "  [0x%08x]     declaration true", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        int typeIdx = getLayoutIndex(classEntry);
        log(context, "  [0x%08x]     containing_type 0x%x (%s)", pos, typeIdx, classEntry.getTypeName());
        pos = writeAttrRef4(typeIdx, buffer, pos);
        if (abbrevCode == AbbrevCode.METHOD_DECLARATION) {
            /* Record the current position so we can back patch the object pointer. */
            int objectPointerIndex = pos;
            /*
             * Write a dummy ref address to move pos on to where the first parameter gets written.
             */
            pos = writeAttrRef4(0, null, pos);
            /*
             * Now backpatch object pointer slot with current pos, identifying the first parameter.
             */
            log(context, "  [0x%08x]     object_pointer 0x%x", objectPointerIndex, pos);
            writeAttrRef4(pos, buffer, objectPointerIndex);
        }
        /* Write method parameter declarations. */
        pos = writeMethodParameterDeclarations(context, classEntry, method, fileIdx, 3, buffer, pos);
        /* write method local declarations */
        pos = writeMethodLocalDeclarations(context, classEntry, method, fileIdx, 3, buffer, pos);
        /*
         * Write a terminating null attribute.
         */
        return writeAttrNull(buffer, pos);
    }

    private int writeMethodParameterDeclarations(DebugContext context, ClassEntry classEntry, MethodEntry method, int fileIdx, int level, byte[] buffer, int p) {
        int pos = p;
        int refAddr;
        if (!Modifier.isStatic(method.getModifiers())) {
            refAddr = pos;
            DebugLocalInfo paramInfo = method.getThisParam();
            setMethodLocalIndex(classEntry, method, paramInfo, refAddr);
            pos = writeMethodParameterDeclaration(context, paramInfo, fileIdx, true, level, buffer, pos);
        }
        for (int i = 0; i < method.getParamCount(); i++) {
            refAddr = pos;
            DebugLocalInfo paramInfo = method.getParam(i);
            setMethodLocalIndex(classEntry, method, paramInfo, refAddr);
            pos = writeMethodParameterDeclaration(context, paramInfo, fileIdx, false, level, buffer, pos);
        }
        return pos;
    }

    private int writeMethodParameterDeclaration(DebugContext context, DebugLocalInfo paramInfo, int fileIdx, boolean artificial, int level, byte[] buffer,
                    int p) {
        int pos = p;
        log(context, "  [0x%08x] method parameter declaration", pos);
        AbbrevCode abbrevCode;
        String paramName = paramInfo.name();
        TypeEntry paramType = lookupType(paramInfo.valueType());
        int line = paramInfo.line();
        if (artificial) {
            abbrevCode = AbbrevCode.METHOD_PARAMETER_DECLARATION_1;
        } else if (line >= 0) {
            abbrevCode = AbbrevCode.METHOD_PARAMETER_DECLARATION_2;
        } else {
            abbrevCode = AbbrevCode.METHOD_PARAMETER_DECLARATION_3;
        }
        log(context, "  [0x%08x] <%d> Abbrev Number %d", pos, level, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     name %s", pos, paramName);
        pos = writeStrSectionOffset(uniqueDebugString(paramName), buffer, pos);
        if (abbrevCode == AbbrevCode.METHOD_PARAMETER_DECLARATION_2) {
            log(context, "  [0x%08x]     file 0x%x", pos, fileIdx);
            pos = writeAttrData2((short) fileIdx, buffer, pos);
            log(context, "  [0x%08x]     line 0x%x", pos, line);
            pos = writeAttrData2((short) line, buffer, pos);
        }
        int typeIdx = getTypeIndex(paramType);
        log(context, "  [0x%08x]     type 0x%x (%s)", pos, typeIdx, paramType.getTypeName());
        pos = writeInfoSectionOffset(typeIdx, buffer, pos);
        if (abbrevCode == AbbrevCode.METHOD_PARAMETER_DECLARATION_1) {
            log(context, "  [0x%08x]     artificial true", pos);
            pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        }
        log(context, "  [0x%08x]     declaration true", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        return pos;
    }

    private int writeMethodLocalDeclarations(DebugContext context, ClassEntry classEntry, MethodEntry method, int fileIdx, int level, byte[] buffer, int p) {
        int pos = p;
        int refAddr;
        for (int i = 0; i < method.getLocalCount(); i++) {
            refAddr = pos;
            DebugLocalInfo localInfo = method.getLocal(i);
            setMethodLocalIndex(classEntry, method, localInfo, refAddr);
            pos = writeMethodLocalDeclaration(context, localInfo, fileIdx, level, buffer, pos);
        }
        return pos;
    }

    private int writeMethodLocalDeclaration(DebugContext context, DebugLocalInfo paramInfo, int fileIdx, int level, byte[] buffer,
                    int p) {
        int pos = p;
        log(context, "  [0x%08x] method local declaration", pos);
        AbbrevCode abbrevCode;
        String paramName = paramInfo.name();
        TypeEntry paramType = lookupType(paramInfo.valueType());
        int line = paramInfo.line();
        if (line >= 0) {
            abbrevCode = AbbrevCode.METHOD_LOCAL_DECLARATION_1;
        } else {
            abbrevCode = AbbrevCode.METHOD_LOCAL_DECLARATION_2;
        }
        log(context, "  [0x%08x] <%d> Abbrev Number %d", pos, level, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     name %s", pos, paramName);
        pos = writeStrSectionOffset(uniqueDebugString(paramName), buffer, pos);
        if (abbrevCode == AbbrevCode.METHOD_LOCAL_DECLARATION_1) {
            log(context, "  [0x%08x]     file 0x%x", pos, fileIdx);
            pos = writeAttrData2((short) fileIdx, buffer, pos);
            log(context, "  [0x%08x]     line 0x%x", pos, line);
            pos = writeAttrData2((short) line, buffer, pos);
        }
        int typeIdx = getTypeIndex(paramType);
        log(context, "  [0x%08x]     type 0x%x (%s)", pos, typeIdx, paramType.getTypeName());
        pos = writeInfoSectionOffset(typeIdx, buffer, pos);
        log(context, "  [0x%08x]     declaration true", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        return pos;
    }

    private int writeInterfaceLayout(DebugContext context, InterfaceClassEntry interfaceClassEntry, byte[] buffer, int p) {
        int pos = p;
        int layoutOffset = pos;
        setLayoutIndex(interfaceClassEntry, layoutOffset);
        log(context, "  [0x%08x] interface layout", pos);
        AbbrevCode abbrevCode = AbbrevCode.INTERFACE_LAYOUT;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        String name = interfaceClassEntry.getTypeName();
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        /*
         * Now write references to all class layouts that implement this interface.
         */
        pos = writeInterfaceImplementors(context, interfaceClassEntry, buffer, pos);
        pos = writeMethodDeclarations(context, interfaceClassEntry, buffer, pos);

        /*
         * Write a terminating null attribute.
         */
        pos = writeAttrNull(buffer, pos);

        if (dwarfSections.useHeapBase()) {
            /*
             * Write a wrapper type with a data_location attribute that can act as a target for an
             * indirect pointer.
             */
            setIndirectLayoutIndex(interfaceClassEntry, pos);
            log(context, "  [0x%08x] indirect class layout", pos);
            abbrevCode = AbbrevCode.INDIRECT_LAYOUT;
            log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
            pos = writeAbbrevCode(abbrevCode, buffer, pos);
            String indirectName = uniqueDebugString(DwarfDebugInfo.INDIRECT_PREFIX + interfaceClassEntry.getTypeName());
            log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(indirectName), name);
            pos = writeStrSectionOffset(indirectName, buffer, pos);
            int size = interfaceClassEntry.getSize();
            log(context, "  [0x%08x]     byte_size 0x%x", pos, size);
            pos = writeAttrData2((short) size, buffer, pos);
            /* Write a data location expression to mask and/or rebase oop pointers. */
            log(context, "  [0x%08x]     data_location", pos);
            pos = writeIndirectOopConversionExpression(false, buffer, pos);
            /* Now write the child field. */
            pos = writeSuperReference(context, layoutOffset, name, buffer, pos);
            /*
             * Write a terminating null attribute.
             */
            pos = writeAttrNull(buffer, pos);
        } else {
            setIndirectLayoutIndex(interfaceClassEntry, layoutOffset);
        }

        return pos;
    }

    private int writeInterfaceImplementors(DebugContext context, InterfaceClassEntry interfaceClassEntry, byte[] buffer, int p) {
        return interfaceClassEntry.implementors().reduce(p,
                        (pos, classEntry) -> writeInterfaceImplementor(context, classEntry, buffer, pos),
                        (oldPos, newPos) -> newPos);
    }

    private int writeInterfaceImplementor(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] interface implementor", pos);
        AbbrevCode abbrevCode = AbbrevCode.INTERFACE_IMPLEMENTOR;
        log(context, "  [0x%08x] <2> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        String name = uniqueDebugString("_" + classEntry.getTypeName());
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        int layoutOffset = getLayoutIndex(classEntry);
        log(context, "  [0x%08x]     type  0x%x (%s)", pos, layoutOffset, classEntry.getTypeName());
        pos = writeInfoSectionOffset(layoutOffset, buffer, pos);
        int modifiers = Modifier.PUBLIC;
        log(context, "  [0x%08x]     modifiers %s", pos, "public");
        pos = writeAttrAccessibility(modifiers, buffer, pos);
        return pos;
    }

    private int writeForeignLayout(DebugContext context, ForeignTypeEntry foreignTypeEntry, byte[] buffer, int p) {
        int pos = p;
        int size = foreignTypeEntry.getSize();
        int layoutOffset = pos;
        if (foreignTypeEntry.isWord()) {
            // define the type as a typedef for a signed or unsigned word i.e. we don't have a
            // layout type
            pos = writeForeignWordLayout(context, foreignTypeEntry, size, foreignTypeEntry.isSigned(), buffer, pos);
        } else if (foreignTypeEntry.isIntegral()) {
            // use a suitably sized signed or unsigned integral type as the layout type
            pos = writeForeignIntegerLayout(context, foreignTypeEntry, size, foreignTypeEntry.isSigned(), buffer, pos);
        } else if (foreignTypeEntry.isFloat()) {
            // use a suitably sized float type as the layout type
            pos = writeForeignFloatLayout(context, foreignTypeEntry, size, buffer, pos);
        } else if (foreignTypeEntry.isStruct()) {
            // define this type using a structure layout
            pos = writeForeignStructLayout(context, foreignTypeEntry, size, buffer, pos);
        } else {
            // this must be a pointer. if the target type is known use it to declare the pointer
            // type, otherwise default to 'void *'
            layoutOffset = voidOffset;
            String referentName = "void";
            if (foreignTypeEntry.isPointer()) {
                TypeEntry pointerTo = foreignTypeEntry.getPointerTo();
                if (pointerTo != null) {
                    layoutOffset = getTypeIndex(foreignTypeEntry.getPointerTo());
                    referentName = foreignTypeEntry.getTypeName();
                }
            }
            log(context, "  [0x%08x] foreign pointer type %s referent 0x%x (%s)", pos, foreignTypeEntry.getTypeName(), layoutOffset, referentName);
        }
        setLayoutIndex(foreignTypeEntry, layoutOffset);

        /*
         * Write declarations for methods of the foreign types as functions
         *
         * n.b. these appear as standalone declarations rather than as children of a class layout
         * DIE so we don't need a terminating attribute.
         */
        pos = writeMethodDeclarations(context, foreignTypeEntry, buffer, pos);
        /*
         * We don't need an indirect type because foreign pointers are never compressed
         */
        setIndirectLayoutIndex(foreignTypeEntry, layoutOffset);

        return pos;
    }

    private int writeForeignStructLayout(DebugContext context, ForeignTypeEntry foreignTypeEntry, int size, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] foreign struct type for %s", pos, foreignTypeEntry.getTypeName());
        AbbrevCode abbrevCode = AbbrevCode.FOREIGN_STRUCT;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        String typedefName = foreignTypeEntry.getTypedefName();
        if (typedefName == null) {
            typedefName = "_" + foreignTypeEntry.getTypeName();
            verboseLog(context, "  [0x%08x]   using synthetic typedef name %s", pos, typedefName);
        }
        if (typedefName.startsWith("struct ")) {
            // log this before correcting it so we have some hope of clearing it up
            log(context, "  [0x%08x]     typedefName includes redundant keyword struct %s", pos, typedefName);
            typedefName = typedefName.substring("struct ".length());
        }
        typedefName = uniqueDebugString(typedefName);
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(typedefName), typedefName);
        pos = writeStrSectionOffset(typedefName, buffer, pos);
        log(context, "  [0x%08x]     byte_size  0x%x", pos, size);
        pos = writeAttrData1((byte) size, buffer, pos);
        // if we have a parent write a super attribute
        ForeignTypeEntry parent = foreignTypeEntry.getParent();
        if (parent != null) {
            int parentOffset = getLayoutIndex(parent);
            pos = writeSuperReference(context, parentOffset, parent.getTypedefName(), buffer, pos);
        }
        pos = writeStructFields(context, foreignTypeEntry.fields(), buffer, pos);
        /*
         * Write a terminating null attribute.
         */
        return writeAttrNull(buffer, pos);
    }

    private int writeForeignWordLayout(DebugContext context, ForeignTypeEntry foreignTypeEntry, int size, boolean isSigned, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] foreign primitive word type for %s", pos, foreignTypeEntry.getTypeName());
        /* Record the location of this type entry. */
        AbbrevCode abbrevCode = AbbrevCode.PRIMITIVE_TYPE;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        assert size >= 0;
        byte byteSize = (byte) (size > 0 ? size : dwarfSections.pointerSize());
        log(context, "  [0x%08x]     byte_size  %d", pos, byteSize);
        pos = writeAttrData1(byteSize, buffer, pos);
        byte bitCount = (byte) (byteSize * 8);
        log(context, "  [0x%08x]     bitCount  %d", pos, bitCount);
        pos = writeAttrData1(bitCount, buffer, pos);
        // treat the layout as a signed or unsigned word of the relevant size
        DwarfEncoding encoding = (isSigned ? DwarfEncoding.DW_ATE_signed : DwarfEncoding.DW_ATE_unsigned);
        log(context, "  [0x%08x]     encoding  0x%x", pos, encoding.value());
        pos = writeAttrEncoding(encoding, buffer, pos);
        String name = uniqueDebugString(integralTypeName(byteSize, isSigned));
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        return writeStrSectionOffset(name, buffer, pos);
    }

    private int writeForeignIntegerLayout(DebugContext context, ForeignTypeEntry foreignTypeEntry, int size, boolean isSigned, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] foreign primitive integral type for %s", pos, foreignTypeEntry.getTypeName());
        /* Record the location of this type entry. */
        AbbrevCode abbrevCode = AbbrevCode.PRIMITIVE_TYPE;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        assert size > 0;
        byte byteSize = (byte) size;
        log(context, "  [0x%08x]     byte_size  %d", pos, byteSize);
        pos = writeAttrData1(byteSize, buffer, pos);
        byte bitCount = (byte) (byteSize * 8);
        log(context, "  [0x%08x]     bitCount  %d", pos, bitCount);
        pos = writeAttrData1(bitCount, buffer, pos);
        // treat the layout as a signed or unsigned word of the relevant size
        DwarfEncoding encoding = (isSigned ? DwarfEncoding.DW_ATE_signed : DwarfEncoding.DW_ATE_unsigned);
        log(context, "  [0x%08x]     encoding  0x%x", pos, encoding.value());
        pos = writeAttrEncoding(encoding, buffer, pos);
        String name = uniqueDebugString(integralTypeName(byteSize, isSigned));
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        return writeStrSectionOffset(name, buffer, pos);
    }

    private int writeForeignFloatLayout(DebugContext context, ForeignTypeEntry foreignTypeEntry, int size, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] foreign primitive float type for %s", pos, foreignTypeEntry.getTypeName());
        /* Record the location of this type entry. */
        AbbrevCode abbrevCode = AbbrevCode.PRIMITIVE_TYPE;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        assert size > 0;
        byte byteSize = (byte) size;
        log(context, "  [0x%08x]     byte_size  %d", pos, byteSize);
        pos = writeAttrData1(byteSize, buffer, pos);
        byte bitCount = (byte) (byteSize * 8);
        log(context, "  [0x%08x]     bitCount  %d", pos, bitCount);
        pos = writeAttrData1(bitCount, buffer, pos);
        // treat the layout as a float of the relevant size
        DwarfEncoding encoding = DwarfEncoding.DW_ATE_float;
        log(context, "  [0x%08x]     encoding  0x%x", pos, encoding.value());
        pos = writeAttrEncoding(encoding, buffer, pos);
        String name = uniqueDebugString(size == 4 ? "float" : (size == 8 ? "double" : "long double"));
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        return writeStrSectionOffset(name, buffer, pos);
    }

    private static String integralTypeName(int byteSize, boolean isSigned) {
        assert (byteSize & (byteSize - 1)) == 0 : "expecting a power of 2!";
        StringBuilder stringBuilder = new StringBuilder();
        if (!isSigned) {
            stringBuilder.append('u');
        }
        stringBuilder.append("int");
        stringBuilder.append(8 * byteSize);
        stringBuilder.append("_t");
        return stringBuilder.toString();
    }

    private int writeClassType(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
        int pos = p;

        /* Define a pointer type referring to the underlying layout. */
        int typeIdx = pos;
        setTypeIndex(classEntry, typeIdx);
        log(context, "  [0x%08x] class pointer type", pos);
        AbbrevCode abbrevCode = AbbrevCode.CLASS_POINTER;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        int pointerSize = dwarfSections.pointerSize();
        log(context, "  [0x%08x]     byte_size 0x%x", pos, pointerSize);
        pos = writeAttrData1((byte) pointerSize, buffer, pos);
        int layoutOffset = getLayoutIndex(classEntry);
        log(context, "  [0x%08x]     type 0x%x", pos, layoutOffset);
        pos = writeAttrRef4(layoutOffset, buffer, pos);

        if (dwarfSections.useHeapBase()) {
            /* Define an indirect pointer type referring to the indirect layout. */
            setIndirectTypeIndex(classEntry, pos);
            log(context, "  [0x%08x] class indirect pointer type", pos);
            abbrevCode = AbbrevCode.INDIRECT_POINTER;
            log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
            pos = writeAbbrevCode(abbrevCode, buffer, pos);
            int oopReferenceSize = dwarfSections.oopReferenceSize();
            log(context, "  [0x%08x]     byte_size 0x%x", pos, oopReferenceSize);
            pos = writeAttrData1((byte) oopReferenceSize, buffer, pos);
            layoutOffset = getIndirectLayoutIndex(classEntry);
            log(context, "  [0x%08x]     type 0x%x", pos, layoutOffset);
            pos = writeAttrRef4(layoutOffset, buffer, pos);
        } else {
            setIndirectTypeIndex(classEntry, typeIdx);
        }

        return pos;
    }

    private int writeInterfaceType(DebugContext context, InterfaceClassEntry interfaceClassEntry, byte[] buffer, int p) {
        int pos = p;

        /* Define a pointer type referring to the underlying layout. */
        int typeIdx = pos;
        setTypeIndex(interfaceClassEntry, typeIdx);
        log(context, "  [0x%08x] interface pointer type", pos);
        AbbrevCode abbrevCode = AbbrevCode.INTERFACE_POINTER;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        int pointerSize = dwarfSections.pointerSize();
        log(context, "  [0x%08x]     byte_size 0x%x", pos, pointerSize);
        pos = writeAttrData1((byte) pointerSize, buffer, pos);
        int layoutOffset = getLayoutIndex(interfaceClassEntry);
        log(context, "  [0x%08x]     type 0x%x", pos, layoutOffset);
        pos = writeAttrRef4(layoutOffset, buffer, pos);

        if (dwarfSections.useHeapBase()) {
            /* Define an indirect pointer type referring to the indirect layout. */
            setIndirectTypeIndex(interfaceClassEntry, pos);
            log(context, "  [0x%08x] interface indirect pointer type", pos);
            abbrevCode = AbbrevCode.INDIRECT_POINTER;
            log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
            pos = writeAbbrevCode(abbrevCode, buffer, pos);
            int byteSize = dwarfSections.oopReferenceSize();
            log(context, "  [0x%08x]     byte_size 0x%x", pos, byteSize);
            pos = writeAttrData1((byte) byteSize, buffer, pos);
            layoutOffset = getIndirectLayoutIndex(interfaceClassEntry);
            log(context, "  [0x%08x]     type 0x%x", pos, layoutOffset);
            pos = writeAttrRef4(layoutOffset, buffer, pos);
        } else {
            setIndirectTypeIndex(interfaceClassEntry, typeIdx);
        }

        return pos;
    }

    private int writeForeignType(DebugContext context, ForeignTypeEntry foreignTypeEntry, byte[] buffer, int p) {
        int pos = p;
        int layoutOffset = getLayoutIndex(foreignTypeEntry);

        // Unlike with Java we use the Java name for the pointer type rather than the
        // underlying base type, or rather for a typedef that targets the pointer type.
        // That ensures that e.g. CCharPointer is a typedef for char*.

        /* Define a pointer type referring to the base type */
        int refTypeIdx = pos;
        log(context, "  [0x%08x] foreign pointer type", pos);
        AbbrevCode abbrevCode = AbbrevCode.FOREIGN_POINTER;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        int pointerSize = dwarfSections.pointerSize();
        log(context, "  [0x%08x]     byte_size 0x%x", pos, pointerSize);
        pos = writeAttrData1((byte) pointerSize, buffer, pos);
        // Note that we write a ref_addr offset here because an unknown (void *)
        // foreign type can have a layout offset that is not in its CU
        log(context, "  [0x%08x]     type 0x%x", pos, layoutOffset);
        pos = writeInfoSectionOffset(layoutOffset, buffer, pos);

        /* Define a typedef for the layout type using the Java name. */
        int typedefIdx = pos;
        log(context, "  [0x%08x] foreign typedef", pos);
        abbrevCode = AbbrevCode.FOREIGN_TYPEDEF;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        String name = uniqueDebugString(foreignTypeEntry.getTypeName());
        log(context, "  [0x%08x]     name %s", pos, name);
        pos = writeStrSectionOffset(name, buffer, pos);
        log(context, "  [0x%08x]     type 0x%x", pos, refTypeIdx);
        pos = writeAttrRef4(refTypeIdx, buffer, pos);

        setTypeIndex(foreignTypeEntry, typedefIdx);
        // foreign pointers are never stored compressed so don't need a separate indirect type
        setIndirectTypeIndex(foreignTypeEntry, typedefIdx);

        return pos;
    }

    private int writeStaticFieldLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
        /*
         * Only write locations for static fields that have an offset greater than 0. A negative
         * offset indicates that the field has been folded into code as an unmaterialized constant.
         */
        Cursor cursor = new Cursor(p);
        classEntry.fields().filter(DwarfInfoSectionImpl::isManifestedStaticField)
                        .forEach(fieldEntry -> {
                            cursor.set(writeClassStaticFieldLocation(context, classEntry, fieldEntry, buffer, cursor.get()));
                        });
        return cursor.get();
    }

    private static boolean isManifestedStaticField(FieldEntry fieldEntry) {
        return Modifier.isStatic(fieldEntry.getModifiers()) && fieldEntry.getOffset() >= 0;
    }

    private int writeClassStaticFieldLocation(DebugContext context, ClassEntry classEntry, FieldEntry fieldEntry, byte[] buffer, int p) {
        int pos = p;
        String fieldName = fieldEntry.fieldName();
        int fieldDefinitionOffset = getFieldDeclarationIndex(classEntry, fieldName);
        log(context, "  [0x%08x] static field location %s.%s", pos, classEntry.getTypeName(), fieldName);
        AbbrevCode abbrevCode = AbbrevCode.STATIC_FIELD_LOCATION;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        // n.b. field definition offset gets written as a Ref4, relative to CU start
        log(context, "  [0x%08x]     specification  0x%x", pos, fieldDefinitionOffset);
        pos = writeAttrRef4(fieldDefinitionOffset, buffer, pos);
        /* Field offset needs to be relocated relative to static primitive or static object base. */
        int offset = fieldEntry.getOffset();
        log(context, "  [0x%08x]     location  heapbase + 0x%x (%s)", pos, offset, (fieldEntry.getValueType().isPrimitive() ? "primitive" : "object"));
        pos = writeHeapLocationExprLoc(offset, buffer, pos);
        return pos;
    }

    private int writeArrays(DebugContext context, byte[] buffer, int p) {
        log(context, "  [0x%08x] array classes", p);
        Cursor cursor = new Cursor(p);
        arrayTypeStream().forEach(arrayTypeEntry -> {
            cuStart = cursor.get();
            cursor.set(writeArray(context, arrayTypeEntry, buffer, cursor.get()));
        });
        return cursor.get();
    }

    private int writeArray(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) {
        int pos = p;
        // Write a Java class unit header
        int lengthPos = pos;
        log(context, "  [0x%08x] Array class unit", pos);
        pos = writeCUHeader(buffer, pos);
        assert pos == lengthPos + DIE_HEADER_SIZE;
        AbbrevCode abbrevCode = AbbrevCode.CLASS_UNIT_1;
        log(context, "  [0x%08x] <0> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     language  %s", pos, "DW_LANG_Java");
        pos = writeAttrLanguage(DwarfDebugInfo.LANG_ENCODING, buffer, pos);
        log(context, "  [0x%08x]     use_UTF8", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        String name = uniqueDebugString("JAVA");
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        String compilationDirectory = dwarfSections.getCachePath();
        log(context, "  [0x%08x]     comp_dir  0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory);
        pos = writeStrSectionOffset(compilationDirectory, buffer, pos);

        /* if the array base type is a class with a loader then embed the children in a namespace */
        String loaderId = arrayTypeEntry.getLoaderId();
        if (!loaderId.isEmpty()) {
            pos = writeNameSpace(context, loaderId, buffer, pos);
        }

        /* Write the array layout and array reference DIEs. */
        TypeEntry elementType = arrayTypeEntry.getElementType();
        int size = arrayTypeEntry.getSize();
        int layoutIdx = pos;
        pos = writeArrayLayout(context, arrayTypeEntry, elementType, size, buffer, pos);
        int indirectLayoutIdx = pos;
        if (dwarfSections.useHeapBase()) {
            pos = writeIndirectArrayLayout(context, arrayTypeEntry, size, layoutIdx, buffer, pos);
        }
        pos = writeArrayTypes(context, arrayTypeEntry, layoutIdx, indirectLayoutIdx, buffer, pos);

        /* Write a declaration for the special Class object pseudo-static field */
        pos = writeClassConstantDeclaration(context, arrayTypeEntry, buffer, pos);

        /* if we opened a namespace then terminate its children */
        if (!loaderId.isEmpty()) {
            pos = writeAttrNull(buffer, pos);
        }

        /*
         * Write a terminating null attribute.
         */
        pos = writeAttrNull(buffer, pos);

        /* Fix up the CU length. */

        patchLength(lengthPos, buffer, pos);
        return pos;
    }

    private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry, TypeEntry elementType, int size, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] array layout", pos);
        AbbrevCode abbrevCode = AbbrevCode.ARRAY_LAYOUT;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        String name = arrayTypeEntry.getTypeName();
        log(context, "  [0x%08x]     name 0x%x (%s)", pos, debugStringIndex(name), name);
        pos = writeStrSectionOffset(name, buffer, pos);
        log(context, "  [0x%08x]     byte_size  0x%x", pos, size);
        pos = writeAttrData2((short) size, buffer, pos);

        /* Now the child DIEs. */

        /* write a type definition for the element array field. */
        int arrayDataTypeIdx = pos;
        pos = writeArrayDataType(context, elementType, buffer, pos);
        pos = writeFields(context, arrayTypeEntry, buffer, pos);
        /* Write a zero length element array field. */
        pos = writeArrayElementField(context, size, arrayDataTypeIdx, buffer, pos);
        pos = writeArraySuperReference(context, buffer, pos);
        /*
         * Write a terminating null attribute.
         */
        return writeAttrNull(buffer, pos);
    }

    private int writeFields(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) {
        Cursor cursor = new Cursor(p);
        arrayTypeEntry.fields().filter(DwarfInfoSectionImpl::isManifestedField)
                        .forEach(fieldEntry -> {
                            cursor.set(writeField(context, arrayTypeEntry, fieldEntry, buffer, cursor.get()));
                        });
        return cursor.get();
    }

    private int writeIndirectArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry, int size, int layoutOffset, byte[] buffer, int p) {
        int pos = p;

        /*
         * write a wrapper type with a data_location attribute that can act as a target for an
         * indirect pointer
         */
        log(context, "  [0x%08x] indirect class layout", pos);
        AbbrevCode abbrevCode = AbbrevCode.INDIRECT_LAYOUT;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        String name = arrayTypeEntry.getTypeName();
        String indirectName = uniqueDebugString(DwarfDebugInfo.INDIRECT_PREFIX + name);
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(indirectName), name);
        pos = writeStrSectionOffset(indirectName, buffer, pos);
        log(context, "  [0x%08x]     byte_size 0x%x", pos, size);
        pos = writeAttrData2((short) size, buffer, pos);
        /* Write a data location expression to mask and/or rebase oop pointers. */
        log(context, "  [0x%08x]     data_location", pos);
        pos = writeIndirectOopConversionExpression(false, buffer, pos);
        /* Now write the child field. */
        pos = writeSuperReference(context, layoutOffset, name, buffer, pos);
        /*
         * Write a terminating null attribute.
         */
        return writeAttrNull(buffer, pos);
    }

    private int writeArrayDataType(DebugContext context, TypeEntry elementType, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] array element data type", pos);
        AbbrevCode abbrevCode = AbbrevCode.ARRAY_DATA_TYPE_1;
        log(context, "  [0x%08x] <2> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        // Java arrays don't have a fixed byte_size
        String elementTypeName = elementType.getTypeName();
        /* use the indirect type for the element type so pointers get translated */
        int elementTypeIdx = getIndirectTypeIndex(elementType);
        log(context, "  [0x%08x]     type idx 0x%x (%s)", pos, elementTypeIdx, elementTypeName);
        pos = writeInfoSectionOffset(elementTypeIdx, buffer, pos);
        return pos;
    }

    private int writeEmbeddedArrayDataType(DebugContext context, ForeignTypeEntry foreignValueType, int valueSize, int arraySize, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] embedded array element data type", pos);
        AbbrevCode abbrevCode = AbbrevCode.ARRAY_DATA_TYPE_2;
        log(context, "  [0x%08x] <2> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        // Foreign arrays have a fixed byte_size
        int size = arraySize * valueSize;
        log(context, "  [0x%08x]     byte_size 0x%x", pos, size);
        pos = writeAttrData4(size, buffer, pos);
        String elementTypeName = foreignValueType.getTypeName();
        int elementTypeIdx;
        if (foreignValueType.isPointer()) {
            TypeEntry pointerTo = foreignValueType.getPointerTo();
            assert pointerTo != null : "ADDRESS field pointer type must have a known target type";
            // type the array using the referent of the pointer type
            //
            // n.b it is critical for correctness to use the index of the referent rather than
            // the layout type of the referring type even though the latter will (eventually)
            // be set to the same value. the type index of the referent is guaranteed to be set
            // on the first sizing pass before it is consumed here on the second writing pass.
            // However, if this embedded struct field definition precedes the definition of the
            // referring type and the latter precedes the definition of the referent type then
            // the layout index of the referring type may still be unset at this point.
            elementTypeIdx = getTypeIndex(pointerTo);
        } else {
            // type the array using the layout type
            elementTypeIdx = getIndirectLayoutIndex(foreignValueType);
        }
        log(context, "  [0x%08x]     type idx 0x%x (%s)", pos, elementTypeIdx, elementTypeName);
        pos = writeInfoSectionOffset(elementTypeIdx, buffer, pos);
        // write subrange child DIE
        log(context, "  [0x%08x] embedded array element range", pos);
        abbrevCode = AbbrevCode.ARRAY_SUBRANGE;
        log(context, "  [0x%08x] <3> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     count 0x%x", pos, arraySize);
        pos = writeAttrData4(arraySize, buffer, pos);
        /*
         * Write a terminating null attribute.
         */
        return writeAttrNull(buffer, pos);
    }

    private int writeArrayElementField(DebugContext context, int offset, int arrayDataTypeIdx, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] array element data field", pos);
        AbbrevCode abbrevCode = AbbrevCode.HEADER_FIELD;
        log(context, "  [0x%08x] <2> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        String fieldName = uniqueDebugString("data");
        log(context, "  [0x%08x]     name  0x%x (%s)", pos, debugStringIndex(fieldName), fieldName);
        pos = writeStrSectionOffset(fieldName, buffer, pos);
        log(context, "  [0x%08x]     type idx 0x%x", pos, arrayDataTypeIdx);
        pos = writeInfoSectionOffset(arrayDataTypeIdx, buffer, pos);
        int size = 0;
        log(context, "  [0x%08x]     offset 0x%x (size 0x%x)", pos, offset, size);
        pos = writeAttrData2((short) offset, buffer, pos);
        int modifiers = Modifier.PUBLIC;
        log(context, "  [0x%08x]     modifiers %s", pos, "public");
        return writeAttrAccessibility(modifiers, buffer, pos);

    }

    private int writeArraySuperReference(DebugContext context, byte[] buffer, int p) {
        int pos = p;
        /* Arrays all inherit from java.lang.Object */
        TypeEntry objectType = lookupObjectClass();
        String superName = objectType.getTypeName();
        assert objectType != null;
        assert objectType instanceof ClassEntry;
        int superOffset = getLayoutIndex((ClassEntry) objectType);
        return writeSuperReference(context, superOffset, superName, buffer, pos);
    }

    private int writeArrayTypes(DebugContext context, ArrayTypeEntry arrayTypeEntry, int layoutOffset, int indirectLayoutOffset, byte[] buffer, int p) {
        int pos = p;
        String name = uniqueDebugString(arrayTypeEntry.getTypeName());

        int typeIdx = pos;
        setTypeIndex(arrayTypeEntry, pos);
        /* Define a pointer type referring to the underlying layout. */
        log(context, "  [0x%08x] array pointer type", pos);
        AbbrevCode abbrevCode = AbbrevCode.ARRAY_POINTER;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        int pointerSize = dwarfSections.pointerSize();
        log(context, "  [0x%08x]     byte_size  0x%x", pos, pointerSize);
        pos = writeAttrData1((byte) pointerSize, buffer, pos);
        log(context, "  [0x%08x]     type (pointer) 0x%x (%s)", pos, layoutOffset, name);
        pos = writeAttrRef4(layoutOffset, buffer, pos);

        if (dwarfSections.useHeapBase()) {
            setIndirectTypeIndex(arrayTypeEntry, pos);
            /* Define an indirect pointer type referring to the underlying indirect layout. */
            log(context, "  [0x%08x] array indirect pointer type", pos);
            abbrevCode = AbbrevCode.INDIRECT_POINTER;
            log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
            pos = writeAbbrevCode(abbrevCode, buffer, pos);
            int byteSize = dwarfSections.oopReferenceSize();
            log(context, "  [0x%08x]     byte_size  0x%x", pos, byteSize);
            pos = writeAttrData1((byte) byteSize, buffer, pos);
            log(context, "  [0x%08x]     type (pointer) 0x%x (%s)", pos, indirectLayoutOffset, name);
            pos = writeAttrRef4(indirectLayoutOffset, buffer, pos);
        } else {
            setIndirectTypeIndex(arrayTypeEntry, typeIdx);
        }

        return pos;
    }

    private int writeMethodLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
        Cursor cursor = new Cursor(p);
        classEntry.compiledEntries().forEach(compiledMethodEntry -> {
            cursor.set(writeMethodLocation(context, classEntry, compiledMethodEntry, buffer, cursor.get()));
        });
        return cursor.get();
    }

    private int writeMethodLocation(DebugContext context, ClassEntry classEntry, CompiledMethodEntry compiledEntry, byte[] buffer, int p) {
        int pos = p;
        Range primary = compiledEntry.getPrimary();
        log(context, "  [0x%08x] method location", pos);
        AbbrevCode abbrevCode = AbbrevCode.METHOD_LOCATION;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     lo_pc  0x%08x", pos, primary.getLo());
        pos = writeAttrAddress(primary.getLo(), buffer, pos);
        log(context, "  [0x%08x]     hi_pc  0x%08x", pos, primary.getHi());
        pos = writeAttrAddress(primary.getHi(), buffer, pos);
        /*
         * Should pass true only if method is non-private.
         */
        log(context, "  [0x%08x]     external  true", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        String methodKey = primary.getSymbolName();
        int methodSpecOffset = getMethodDeclarationIndex(primary.getMethodEntry());
        log(context, "  [0x%08x]     specification  0x%x (%s)", pos, methodSpecOffset, methodKey);
        pos = writeAttrRef4(methodSpecOffset, buffer, pos);
        HashMap> varRangeMap = primary.getVarRangeMap();
        pos = writeMethodParameterLocations(context, classEntry, varRangeMap, primary, 2, buffer, pos);
        pos = writeMethodLocalLocations(context, classEntry, varRangeMap, primary, 2, buffer, pos);
        if (primary.includesInlineRanges()) {
            /*
             * the method has inlined ranges so write concrete inlined method entries as its
             * children
             */
            pos = generateConcreteInlinedMethods(context, classEntry, compiledEntry, buffer, pos);
        }
        /*
         * Write a terminating null attribute.
         */
        return writeAttrNull(buffer, pos);
    }

    private int writeMethodParameterLocations(DebugContext context, ClassEntry classEntry, HashMap> varRangeMap, Range range, int depth, byte[] buffer, int p) {
        int pos = p;
        MethodEntry methodEntry;
        if (range.isPrimary()) {
            methodEntry = range.getMethodEntry();
        } else {
            assert !range.isLeaf() : "should only be looking up var ranges for inlined calls";
            methodEntry = range.getFirstCallee().getMethodEntry();
        }
        if (!Modifier.isStatic(methodEntry.getModifiers())) {
            DebugLocalInfo thisParamInfo = methodEntry.getThisParam();
            int refAddr = getMethodLocalIndex(classEntry, methodEntry, thisParamInfo);
            List ranges = varRangeMap.get(thisParamInfo);
            pos = writeMethodLocalLocation(context, range, thisParamInfo, refAddr, ranges, depth, true, buffer, pos);
        }
        for (int i = 0; i < methodEntry.getParamCount(); i++) {
            DebugLocalInfo paramInfo = methodEntry.getParam(i);
            int refAddr = getMethodLocalIndex(classEntry, methodEntry, paramInfo);
            List ranges = varRangeMap.get(paramInfo);
            pos = writeMethodLocalLocation(context, range, paramInfo, refAddr, ranges, depth, true, buffer, pos);
        }
        return pos;
    }

    private int writeMethodLocalLocations(DebugContext context, ClassEntry classEntry, HashMap> varRangeMap, Range range, int depth, byte[] buffer, int p) {
        int pos = p;
        MethodEntry methodEntry;
        if (range.isPrimary()) {
            methodEntry = range.getMethodEntry();
        } else {
            assert !range.isLeaf() : "should only be looking up var ranges for inlined calls";
            methodEntry = range.getFirstCallee().getMethodEntry();
        }
        int count = methodEntry.getLocalCount();
        for (int i = 0; i < count; i++) {
            DebugLocalInfo localInfo = methodEntry.getLocal(i);
            int refAddr = getMethodLocalIndex(classEntry, methodEntry, localInfo);
            List ranges = varRangeMap.get(localInfo);
            pos = writeMethodLocalLocation(context, range, localInfo, refAddr, ranges, depth, false, buffer, pos);
        }
        return pos;
    }

    private int writeMethodLocalLocation(DebugContext context, Range range, DebugLocalInfo localInfo, int refAddr, List ranges, int depth, boolean isParam, byte[] buffer,
                    int p) {
        int pos = p;
        log(context, "  [0x%08x] method %s location %s:%s", pos, (isParam ? "parameter" : "local"), localInfo.name(), localInfo.typeName());
        List localValues = new ArrayList<>();
        for (SubRange subrange : ranges) {
            DebugLocalValueInfo value = subrange.lookupValue(localInfo);
            if (value != null) {
                log(context, "  [0x%08x]     local  %s:%s [0x%x, 0x%x] = %s", pos, value.name(), value.typeName(), subrange.getLo(), subrange.getHi(), formatValue(value));
                switch (value.localKind()) {
                    case REGISTER:
                    case STACKSLOT:
                        localValues.add(value);
                        break;
                    case CONSTANT:
                        JavaConstant constant = value.constantValue();
                        // can only handle primitive or null constants just now
                        if (constant instanceof PrimitiveConstant || constant.getJavaKind() == JavaKind.Object) {
                            localValues.add(value);
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        AbbrevCode abbrevCode;
        if (localValues.isEmpty()) {
            abbrevCode = (isParam ? AbbrevCode.METHOD_PARAMETER_LOCATION_1 : AbbrevCode.METHOD_LOCAL_LOCATION_1);
        } else {
            abbrevCode = (isParam ? AbbrevCode.METHOD_PARAMETER_LOCATION_2 : AbbrevCode.METHOD_LOCAL_LOCATION_2);
        }
        log(context, "  [0x%08x] <%d> Abbrev Number %d", pos, depth, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     specification  0x%x", pos, refAddr);
        pos = writeAttrRef4(refAddr, buffer, pos);
        if (abbrevCode == AbbrevCode.METHOD_LOCAL_LOCATION_2 ||
                        abbrevCode == AbbrevCode.METHOD_PARAMETER_LOCATION_2) {
            int locRefAddr = getRangeLocalIndex(range, localInfo);
            log(context, "  [0x%08x]     loc list  0x%x", pos, locRefAddr);
            pos = writeLocSectionOffset(locRefAddr, buffer, pos);
        }
        return pos;
    }

    /**
     * Go through the subranges and generate concrete debug entries for inlined methods.
     */
    private int generateConcreteInlinedMethods(DebugContext context, ClassEntry classEntry, CompiledMethodEntry compiledEntry, byte[] buffer, int p) {
        Range primary = compiledEntry.getPrimary();
        if (primary.isLeaf()) {
            return p;
        }
        int pos = p;
        log(context, "  [0x%08x] concrete entries [0x%x,0x%x] %s", pos, primary.getLo(), primary.getHi(), primary.getFullMethodName());
        int depth = 0;
        Iterator iterator = compiledEntry.topDownRangeIterator();
        while (iterator.hasNext()) {
            SubRange subrange = iterator.next();
            if (subrange.isLeaf()) {
                // we only generate concrete methods for non-leaf entries
                continue;
            }
            // if we just stepped out of a child range write nulls for each step up
            while (depth > subrange.getDepth()) {
                pos = writeAttrNull(buffer, pos);
                depth--;
            }
            depth = subrange.getDepth();
            pos = writeInlineSubroutine(context, classEntry, subrange, depth + 2, buffer, pos);
            HashMap> varRangeMap = subrange.getVarRangeMap();
            // increment depth to account for parameter and method locations
            depth++;
            pos = writeMethodParameterLocations(context, classEntry, varRangeMap, subrange, depth + 2, buffer, pos);
            pos = writeMethodLocalLocations(context, classEntry, varRangeMap, subrange, depth + 2, buffer, pos);
        }
        // if we just stepped out of a child range write nulls for each step up
        while (depth > 0) {
            pos = writeAttrNull(buffer, pos);
            depth--;
        }
        return pos;
    }

    private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, SubRange caller, int depth, byte[] buffer, int p) {
        assert !caller.isLeaf();
        // the supplied range covers an inline call and references the caller method entry. its
        // child ranges all reference the same inlined called method. leaf children cover code for
        // that inlined method. non-leaf children cover code for recursively inlined methods.
        // identify the inlined method by looking at the first callee
        Range callee = caller.getFirstCallee();
        MethodEntry methodEntry = callee.getMethodEntry();
        String methodKey = methodEntry.getSymbolName();
        /* the abstract index was written in the method's class entry */
        int abstractOriginIndex = (classEntry == methodEntry.ownerType() ? getMethodDeclarationIndex(methodEntry) : getAbstractInlineMethodIndex(classEntry, methodEntry));

        int pos = p;
        log(context, "  [0x%08x] concrete inline subroutine [0x%x, 0x%x] %s", pos, caller.getLo(), caller.getHi(), methodKey);

        int callLine = caller.getLine();
        assert callLine >= -1 : callLine;
        int fileIndex;
        if (callLine == -1) {
            log(context, "  Unable to retrieve call line for inlined method %s", callee.getFullMethodName());
            /* continue with line 0 and fileIndex 1 as we must insert a tree node */
            callLine = 0;
            fileIndex = classEntry.getFileIdx();
        } else {
            FileEntry subFileEntry = caller.getFileEntry();
            if (subFileEntry != null) {
                fileIndex = classEntry.getFileIdx(subFileEntry);
            } else {
                log(context, "  Unable to retrieve caller FileEntry for inlined method %s (caller method %s)", callee.getFullMethodName(), caller.getFullMethodName());
                fileIndex = classEntry.getFileIdx();
            }
        }
        final AbbrevCode abbrevCode = AbbrevCode.INLINED_SUBROUTINE_WITH_CHILDREN;
        log(context, "  [0x%08x] <%d> Abbrev Number %d", pos, depth, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     abstract_origin  0x%x", pos, abstractOriginIndex);
        pos = writeAttrRef4(abstractOriginIndex, buffer, pos);
        log(context, "  [0x%08x]     lo_pc  0x%08x", pos, caller.getLo());
        pos = writeAttrAddress(caller.getLo(), buffer, pos);
        log(context, "  [0x%08x]     hi_pc  0x%08x", pos, caller.getHi());
        pos = writeAttrAddress(caller.getHi(), buffer, pos);
        log(context, "  [0x%08x]     call_file  %d", pos, fileIndex);
        pos = writeAttrData4(fileIndex, buffer, pos);
        log(context, "  [0x%08x]     call_line  %d", pos, callLine);
        pos = writeAttrData4(callLine, buffer, pos);
        return pos;
    }

    private int writeAbstractInlineMethods(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
        EconomicSet inlinedMethods = collectInlinedMethods(context, classEntry, p);
        int pos = p;
        for (MethodEntry methodEntry : inlinedMethods) {
            // n.b. class entry used to index the method belongs to the inlining method
            // not the inlined method
            setAbstractInlineMethodIndex(classEntry, methodEntry, pos);
            pos = writeAbstractInlineMethod(context, classEntry, methodEntry, buffer, pos);
        }
        return pos;
    }

    private EconomicSet collectInlinedMethods(DebugContext context, ClassEntry classEntry, int p) {
        final EconomicSet methods = EconomicSet.create();
        classEntry.compiledEntries().forEach(compiledEntry -> addInlinedMethods(context, compiledEntry, compiledEntry.getPrimary(), methods, p));
        return methods;
    }

    private void addInlinedMethods(DebugContext context, CompiledMethodEntry compiledEntry, Range primary, EconomicSet hashSet, int p) {
        if (primary.isLeaf()) {
            return;
        }
        verboseLog(context, "  [0x%08x] collect abstract inlined methods %s", p, primary.getFullMethodName());
        Iterator iterator = compiledEntry.topDownRangeIterator();
        while (iterator.hasNext()) {
            SubRange subrange = iterator.next();
            if (subrange.isLeaf()) {
                // we only generate abstract inline methods for non-leaf entries
                continue;
            }
            // the subrange covers an inline call and references the caller method entry. its
            // child ranges all reference the same inlined called method. leaf children cover code
            // for
            // that inlined method. non-leaf children cover code for recursively inlined methods.
            // identify the inlined method by looking at the first callee
            Range callee = subrange.getFirstCallee();
            MethodEntry methodEntry = callee.getMethodEntry();
            if (hashSet.add(methodEntry)) {
                verboseLog(context, "  [0x%08x]   add abstract inlined method %s", p, methodEntry.getSymbolName());
            }
        }
    }

    private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntry, MethodEntry method, byte[] buffer, int p) {
        int pos = p;
        log(context, "  [0x%08x] abstract inline method %s::%s", pos, classEntry.getTypeName(), method.methodName());
        AbbrevCode abbrevCode = AbbrevCode.ABSTRACT_INLINE_METHOD;
        log(context, "  [0x%08x] <1> Abbrev Number %d", pos, abbrevCode.ordinal());
        pos = writeAbbrevCode(abbrevCode, buffer, pos);
        log(context, "  [0x%08x]     inline  0x%x", pos, DwarfInline.DW_INL_inlined.value());
        pos = writeAttrInline(DwarfInline.DW_INL_inlined, buffer, pos);
        /*
         * Should pass true only if method is non-private.
         */
        log(context, "  [0x%08x]     external  true", pos);
        pos = writeFlag(DwarfFlag.DW_FLAG_true, buffer, pos);
        int methodSpecOffset = getMethodDeclarationIndex(method);
        log(context, "  [0x%08x]     specification  0x%x", pos, methodSpecOffset);
        pos = writeInfoSectionOffset(methodSpecOffset, buffer, pos);
        /*
         * If the inline method exists in a different CU then write locals and params otherwise we
         * can just reuse the locals and params in the declaration
         */
        if (classEntry != method.ownerType()) {
            FileEntry fileEntry = method.getFileEntry();
            if (fileEntry == null) {
                fileEntry = classEntry.getFileEntry();
            }
            assert fileEntry != null;
            int fileIdx = classEntry.getFileIdx(fileEntry);
            int level = 3;
            // n.b. class entry used to index the params belongs to the inlining method
            // not the inlined method
            pos = writeMethodParameterDeclarations(context, classEntry, method, fileIdx, level, buffer, pos);
            pos = writeMethodLocalDeclarations(context, classEntry, method, fileIdx, level, buffer, pos);
        }
        /*
         * Write a terminating null attribute.
         */
        return writeAttrNull(buffer, pos);
    }

    private int writeAttrRef4(int reference, byte[] buffer, int p) {
        // make sure we have actually started writing a CU
        assert cuStart >= 0;
        // writes a CU-relative offset
        return writeAttrData4(reference - cuStart, buffer, p);
    }

    private int writeCUHeader(byte[] buffer, int p) {
        int pos = p;
        /* CU length. */
        pos = writeInt(0, buffer, pos);
        /* DWARF version. */
        pos = writeDwarfVersion(DwarfVersion.DW_VERSION_4, buffer, pos);
        /* Abbrev offset. */
        pos = writeAbbrevSectionOffset(0, buffer, pos);
        /* Address size. */
        return writeByte((byte) 8, buffer, pos);
    }

    @SuppressWarnings("unused")
    public int writeAttrString(String value, byte[] buffer, int p) {
        int pos = p;
        return writeUTF8StringBytes(value, buffer, pos);
    }

    public int writeAttrLanguage(DwarfLanguage language, byte[] buffer, int p) {
        int pos = p;
        return writeByte(language.value(), buffer, pos);
    }

    public int writeAttrEncoding(DwarfEncoding encoding, byte[] buffer, int p) {
        return writeByte(encoding.value(), buffer, p);
    }

    public int writeAttrInline(DwarfInline inline, byte[] buffer, int p) {
        return writeByte(inline.value(), buffer, p);
    }

    public int writeAttrAccessibility(int modifiers, byte[] buffer, int p) {
        DwarfAccess access;
        if (Modifier.isPublic(modifiers)) {
            access = DwarfAccess.DW_ACCESS_public;
        } else if (Modifier.isProtected(modifiers)) {
            access = DwarfAccess.DW_ACCESS_protected;
        } else if (Modifier.isPrivate(modifiers)) {
            access = DwarfAccess.DW_ACCESS_private;
        } else {
            /* Actually package private -- make it public for now. */
            access = DwarfAccess.DW_ACCESS_public;
        }
        return writeByte(access.value(), buffer, p);
    }

    public int writeIndirectOopConversionExpression(boolean isHub, byte[] buffer, int p) {
        int pos = p;
        /*
         * For an explanation of the conversion rules @see com.oracle.svm.core.heap.ReferenceAccess
         *
         * n.b.
         *
         * The setting for option -H:+/-SpawnIsolates is determined by useHeapBase == true/false.
         *
         */

        boolean useHeapBase = dwarfSections.useHeapBase();
        int oopCompressShift = dwarfSections.oopCompressShift();
        int oopTagsShift = dwarfSections.oopTagsShift();
        int oopAlignShift = dwarfSections.oopAlignShift();
        /* we may be able to use a mask or a right shift then a left shift or just a left shift */
        int mask = 0;
        int rightShift = 0;
        int leftShift = 0;
        int exprSize = 0;

        /*
         * First we compute the size of the locexpr and decide how to do any required bit-twiddling
         */
        if (!useHeapBase) {
            /* We must be compressing for a hub otherwise this call would not be needed. */
            assert isHub == true;
            mask = dwarfSections.oopTagsMask();
            assert mask != 0;
            /*-
             * We don't need to care about zero oops just mask off the tag bits.
             *
             * required expression is
             *
             *  .... push object address .. (1 byte) ..... [tagged oop]
             *  .... push mask ............ (1 byte) ..... [tagged oop, mask]
             *  .... NOT .................. (1 byte) ..... [tagged oop, ~mask]
             *  .... AND .................. (1 byte) ..... [raw oop]
             */
            exprSize += 4;
        } else {
            /*-
             * required expression will be one of these paths
             *
             *  .... push object address .. (1 byte) ..... [offset]
             *  .... duplicate object base  (1 byte) ..... [offset, offset]
             *  .... push 0 ............... (1 byte) ..... [offset, offset, 0]
             *  .... eq ................... (1 byte) ..... [offset]
             *  .... brtrue end ........... (3 bytes) .... [offset == oop == 0 if taken]
             *  IF mask != 0
             *  .... push mask ............ (1 byte) ..... [offset, mask]
             *  .... NOT .................. (1 byte) ..... [offset, ~mask]
             *  .... AND .................. (1 byte) ..... [offset]
             *  ELSE
             *    IF rightShift != 0
             *  .... push rightShift ...... (1 byte) ..... [offset, right shift]
             *  .... LSHR ................. (1 byte) ..... [offset]
             *    END IF
             *    IF leftShift != 0
             *  .... push leftShift ....... (1 byte) ..... [offset, left shift]
             *  .... LSHL ................. (1 byte) ..... [offset]
             *    END IF
             *  END IF
             *  .... push rheap+0 ......... (2 bytes) .... [offset, rheap]
             *  .... ADD .................. (1 byte) ..... [oop]
             * end: ...................................... [oop]
             *
             */
            /* Count all bytes in common path */
            exprSize += 10;
            if (isHub) {
                if (oopCompressShift == 0) {
                    /* We need to use oopAlignment for the shift. */
                    oopCompressShift = oopAlignShift;
                }
                if (oopCompressShift == oopTagsShift) {
                    /* We can use a mask to remove the bits. */
                    mask = dwarfSections.oopTagsMask();
                    exprSize += 3;
                } else {
                    /* We need one or two shifts to remove the bits. */
                    if (oopTagsShift != 0) {
                        rightShift = oopTagsShift;
                        exprSize += 2;
                    }
                    leftShift = oopCompressShift;
                    exprSize += 2;
                }
            } else {
                /* No flags to deal with, so we need either an uncompress or nothing. */
                if (oopCompressShift != 0) {
                    leftShift = oopCompressShift;
                    exprSize += 2;
                }
            }
        }
        /* Write size followed by the expression and check the size comes out correct. */
        pos = writeULEB(exprSize, buffer, pos);
        int exprStart = pos;
        if (!useHeapBase) {
            pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_push_object_address, buffer, pos);
            pos = writeExprOpcodeLiteral(mask, buffer, pos);
            pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_not, buffer, pos);
            pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_and, buffer, pos);
        } else {
            pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_push_object_address, buffer, pos);
            /* skip to end if oop is null */
            pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_dup, buffer, pos);
            pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_lit0, buffer, pos);
            pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_eq, buffer, pos);
            int skipStart = pos + 3; /* offset excludes BR op + 2 operand bytes */
            short offsetToEnd = (short) (exprSize - (skipStart - exprStart));
            pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_bra, buffer, pos);
            pos = writeShort(offsetToEnd, buffer, pos);
            /* insert mask or shifts as necessary */
            if (mask != 0) {
                pos = writeExprOpcodeLiteral(mask, buffer, pos);
                pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_not, buffer, pos);
                pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_and, buffer, pos);
            } else {
                if (rightShift != 0) {
                    pos = writeExprOpcodeLiteral(rightShift, buffer, pos);
                    pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_shr, buffer, pos);
                }
                if (leftShift != 0) {
                    pos = writeExprOpcodeLiteral(leftShift, buffer, pos);
                    pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_shl, buffer, pos);
                }
            }
            /* add the resulting offset to the heapbase register */
            pos = writeExprOpcodeBReg(dwarfSections.getHeapbaseRegister(), buffer, pos);
            pos = writeSLEB(0, buffer, pos); /* 1 byte. */
            pos = writeExprOpcode(DwarfExpressionOpcode.DW_OP_plus, buffer, pos);
            assert pos == skipStart + offsetToEnd;

            /* make sure we added up correctly */
            assert pos == exprStart + exprSize;
        }
        return pos;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy