New @bitCast Semantics and LLVM Backend Improvements
Author: Matthew Lugg
This is nifty — I’ve been writing some endiannes-sketchy code recently, and looks like it will magically come out endianness-clean from just upgrading the compiler?
Just to clarify, if I want to have the old transmute/re-interpret memory semantics, that is still available via a pointer cast?
This in incredibly interesting.
I have some bit-packed values in a network protocol, and being able to @bitCast the incoming bytes to something like [6]i20 would be massively convenient.
Based changes.
I have somehow narrowly avoided this causing disasterous uncaught changes to my code on big endian targets because I switched to using std.mem.writeInt on the backing integer a long time ago. Big endian targets actually existing seems to be a myth though.
/// Convert little endian packed bytes from EtherCAT to host representation.
///
/// Supports enums, packed structs, and most primitive types. All must have
/// bitlength divisible by 8.
pub fn packFromECat(comptime T: type, ecat_bytes: *const [@divExact(@bitSizeOf(T), 8)]u8) T {
comptime assert(isECatPackable(T));
return switch (@typeInfo(T)) {
.@"enum" => |info| @enumFromInt(std.mem.readInt(
info.tag_type,
ecat_bytes,
.little,
)),
else => @bitCast(std.mem.readInt(
@Int(.unsigned, @bitSizeOf(T)),
ecat_bytes,
.little,
)),
};
}
Edit: well actually is this going to reverse arrays here now? Example being using this function to write an array on a big endian target.
/// convert a packed struct to bytes that can be sent via ethercat
///
/// the packed struct must have bitwidth that is a multiple of 8
pub fn eCatFromPack(pack: anytype) [@divExact(@bitSizeOf(@TypeOf(pack)), 8)]u8 {
comptime assert(isECatPackable(@TypeOf(pack)));
const int = switch (@typeInfo(@TypeOf(pack))) {
.@"enum" => |info| @as(info.tag_type, @intFromEnum(pack)),
else => @as(
@Int(.unsigned, @bitSizeOf(@TypeOf(pack))),
@bitCast(pack),
),
};
var bytes: [@divExact(@bitSizeOf(@TypeOf(pack)), 8)]u8 = undefined;
std.mem.writeInt(
@TypeOf(int),
&bytes,
int,
.little,
);
return bytes;
}
(Quite long devlog coming up, apologies—I got a little carried away with this one!)
I don’t hear anyone complaining ![]()
Yep, you can always use a classic @ptrCast or extern union approach to type pun in memory. The only thing to be aware of is that it isn’t quite equivalent to the old @bitCast semantics due to things like [3]u8 → u24—type punning there results in illegal behaviour or an undefined value (depending on your strategy), because @sizeOf(u24) != @sizeOf([3]u8). In that particular case you do need to use @bitCast for the conversion (with a @byteSwap or whatever to switch endianness if needed).
massive
for the nonnative integer lowering changes, i have been waiting for that so i can confidently use those types without worrying about the slowdown
i am generally happy about the changes to @bitCast as well – the downside, of course, being that on big-endian targets the operation is now more involved