Source
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()` 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);
}
};
}