DoxigAlpha

Builder

Zero-operand instructions

Functions

#
writeOpcode
Zero-operand instructions

Source

Implementation

#
pub fn Builder(comptime options: Options) type {
    const addr_type = switch (options.addr_size) {
        2 => u16,
        4 => u32,
        8 => u64,
        else => @compileError("Unsupported address size of " ++ options.addr_size),
    };

    return struct {
        /// Zero-operand instructions
        pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void {
            if (options.call_frame_context and !comptime isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
            switch (opcode) {
                OP.dup,
                OP.drop,
                OP.over,
                OP.swap,
                OP.rot,
                OP.deref,
                OP.xderef,
                OP.push_object_address,
                OP.form_tls_address,
                OP.call_frame_cfa,
                OP.abs,
                OP.@"and",
                OP.div,
                OP.minus,
                OP.mod,
                OP.mul,
                OP.neg,
                OP.not,
                OP.@"or",
                OP.plus,
                OP.shl,
                OP.shr,
                OP.shra,
                OP.xor,
                OP.le,
                OP.ge,
                OP.eq,
                OP.lt,
                OP.gt,
                OP.ne,
                OP.nop,
                OP.stack_value,
                => try writer.writeByte(opcode),
                else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),
            }
        }

        // 2.5.1.1: Literal Encodings
        pub fn writeLiteral(writer: anytype, literal: u8) !void {
            switch (literal) {
                0...31 => |n| try writer.writeByte(n + OP.lit0),
                else => return error.InvalidLiteral,
            }
        }

        pub fn writeConst(writer: anytype, comptime T: type, value: T) !void {
            if (@typeInfo(T) != .int) @compileError("Constants must be integers");

            switch (T) {
                u8, i8, u16, i16, u32, i32, u64, i64 => {
                    try writer.writeByte(switch (T) {
                        u8 => OP.const1u,
                        i8 => OP.const1s,
                        u16 => OP.const2u,
                        i16 => OP.const2s,
                        u32 => OP.const4u,
                        i32 => OP.const4s,
                        u64 => OP.const8u,
                        i64 => OP.const8s,
                        else => unreachable,
                    });

                    try writer.writeInt(T, value, options.endian);
                },
                else => switch (@typeInfo(T).int.signedness) {
                    .unsigned => {
                        try writer.writeByte(OP.constu);
                        try leb.writeUleb128(writer, value);
                    },
                    .signed => {
                        try writer.writeByte(OP.consts);
                        try leb.writeIleb128(writer, value);
                    },
                },
            }
        }

        pub fn writeConstx(writer: anytype, debug_addr_offset: anytype) !void {
            try writer.writeByte(OP.constx);
            try leb.writeUleb128(writer, debug_addr_offset);
        }

        pub fn writeConstType(writer: anytype, die_offset: anytype, value_bytes: []const u8) !void {
            if (options.call_frame_context) return error.InvalidCFAOpcode;
            if (value_bytes.len > 0xff) return error.InvalidTypeLength;
            try writer.writeByte(OP.const_type);
            try leb.writeUleb128(writer, die_offset);
            try writer.writeByte(@intCast(value_bytes.len));
            try writer.writeAll(value_bytes);
        }

        pub fn writeAddr(writer: anytype, value: addr_type) !void {
            try writer.writeByte(OP.addr);
            try writer.writeInt(addr_type, value, options.endian);
        }

        pub fn writeAddrx(writer: anytype, debug_addr_offset: anytype) !void {
            if (options.call_frame_context) return error.InvalidCFAOpcode;
            try writer.writeByte(OP.addrx);
            try leb.writeUleb128(writer, debug_addr_offset);
        }

        // 2.5.1.2: Register Values
        pub fn writeFbreg(writer: anytype, offset: anytype) !void {
            try writer.writeByte(OP.fbreg);
            try leb.writeIleb128(writer, offset);
        }

        pub fn writeBreg(writer: anytype, register: u8, offset: anytype) !void {
            if (register > 31) return error.InvalidRegister;
            try writer.writeByte(OP.breg0 + register);
            try leb.writeIleb128(writer, offset);
        }

        pub fn writeBregx(writer: anytype, register: anytype, offset: anytype) !void {
            try writer.writeByte(OP.bregx);
            try leb.writeUleb128(writer, register);
            try leb.writeIleb128(writer, offset);
        }

        pub fn writeRegvalType(writer: anytype, register: anytype, offset: anytype) !void {
            if (options.call_frame_context) return error.InvalidCFAOpcode;
            try writer.writeByte(OP.regval_type);
            try leb.writeUleb128(writer, register);
            try leb.writeUleb128(writer, offset);
        }

        // 2.5.1.3: Stack Operations
        pub fn writePick(writer: anytype, index: u8) !void {
            try writer.writeByte(OP.pick);
            try writer.writeByte(index);
        }

        pub fn writeDerefSize(writer: anytype, size: u8) !void {
            try writer.writeByte(OP.deref_size);
            try writer.writeByte(size);
        }

        pub fn writeXDerefSize(writer: anytype, size: u8) !void {
            try writer.writeByte(OP.xderef_size);
            try writer.writeByte(size);
        }

        pub fn writeDerefType(writer: anytype, size: u8, die_offset: anytype) !void {
            if (options.call_frame_context) return error.InvalidCFAOpcode;
            try writer.writeByte(OP.deref_type);
            try writer.writeByte(size);
            try leb.writeUleb128(writer, die_offset);
        }

        pub fn writeXDerefType(writer: anytype, size: u8, die_offset: anytype) !void {
            try writer.writeByte(OP.xderef_type);
            try writer.writeByte(size);
            try leb.writeUleb128(writer, die_offset);
        }

        // 2.5.1.4: Arithmetic and Logical Operations

        pub fn writePlusUconst(writer: anytype, uint_value: anytype) !void {
            try writer.writeByte(OP.plus_uconst);
            try leb.writeUleb128(writer, uint_value);
        }

        // 2.5.1.5: Control Flow Operations

        pub fn writeSkip(writer: anytype, offset: i16) !void {
            try writer.writeByte(OP.skip);
            try writer.writeInt(i16, offset, options.endian);
        }

        pub fn writeBra(writer: anytype, offset: i16) !void {
            try writer.writeByte(OP.bra);
            try writer.writeInt(i16, offset, options.endian);
        }

        pub fn writeCall(writer: anytype, comptime T: type, offset: T) !void {
            if (options.call_frame_context) return error.InvalidCFAOpcode;
            switch (T) {
                u16 => try writer.writeByte(OP.call2),
                u32 => try writer.writeByte(OP.call4),
                else => @compileError("Call operand must be a 2 or 4 byte offset"),
            }

            try writer.writeInt(T, offset, options.endian);
        }

        pub fn writeCallRef(writer: anytype, comptime is_64: bool, value: if (is_64) u64 else u32) !void {
            if (options.call_frame_context) return error.InvalidCFAOpcode;
            try writer.writeByte(OP.call_ref);
            try writer.writeInt(if (is_64) u64 else u32, value, options.endian);
        }

        pub fn writeConvert(writer: anytype, die_offset: anytype) !void {
            if (options.call_frame_context) return error.InvalidCFAOpcode;
            try writer.writeByte(OP.convert);
            try leb.writeUleb128(writer, die_offset);
        }

        pub fn writeReinterpret(writer: anytype, die_offset: anytype) !void {
            if (options.call_frame_context) return error.InvalidCFAOpcode;
            try writer.writeByte(OP.reinterpret);
            try leb.writeUleb128(writer, die_offset);
        }

        // 2.5.1.7: Special Operations

        pub fn writeEntryValue(writer: anytype, expression: []const u8) !void {
            try writer.writeByte(OP.entry_value);
            try leb.writeUleb128(writer, expression.len);
            try writer.writeAll(expression);
        }

        // 2.6: Location Descriptions
        pub fn writeReg(writer: anytype, register: u8) !void {
            try writer.writeByte(OP.reg0 + register);
        }

        pub fn writeRegx(writer: anytype, register: anytype) !void {
            try writer.writeByte(OP.regx);
            try leb.writeUleb128(writer, register);
        }

        pub fn writeImplicitValue(writer: anytype, value_bytes: []const u8) !void {
            try writer.writeByte(OP.implicit_value);
            try leb.writeUleb128(writer, value_bytes.len);
            try writer.writeAll(value_bytes);
        }
    };
}