| 1 | //! This file contains logic for bit-casting arbitrary values at comptime, including splicing |
| 2 | //! bits together for comptime stores of bit-pointers. The strategy is to "flatten" values to |
| 3 | //! a sequence of values in *packed* memory, and then unflatten through a combination of special |
| 4 | //! cases (particularly for pointers and `undefined` values) and in-memory buffer reinterprets. |
| 5 | //! |
| 6 | //! This is a little awkward on big-endian targets, as non-packed datastructures (e.g. `extern struct`) |
| 7 | //! have their fields reversed when represented as packed memory on such targets. |
| 8 | |
| 9 | /// If `host_bits` is `0`, attempts to convert the memory at offset |
| 10 | /// `byte_offset` into `val` to a non-packed value of type `dest_ty`, |
| 11 | /// ignoring `bit_offset`. |
| 12 | /// |
| 13 | /// Otherwise, `byte_offset` is an offset in bytes into `val` to a |
| 14 | /// non-packed value consisting of `host_bits` bits. A value of type |
| 15 | /// `dest_ty` will be interpreted at a packed offset of `bit_offset` |
| 16 | /// into this value. |
| 17 | /// |
| 18 | /// Returns `null` if the operation must be performed at runtime. |
| 19 | pub fn castMemory( |
| 20 | sema: *Sema, |
| 21 | val: Value, |
| 22 | dest_ty: Type, |
| 23 | byte_offset: u64, |
| 24 | ) CompileError!?Value { |
| 25 | const pt = sema.pt; |
| 26 | const zcu = pt.zcu; |
| 27 | |
| 28 | const val_ty = val.typeOf(zcu); |
| 29 | |
| 30 | if (dest_ty.toIntern() == val_ty.toIntern()) { |
| 31 | assert(byte_offset == 0); |
| 32 | return val; |
| 33 | } |
| 34 | |
| 35 | val_ty.assertHasLayout(zcu); |
| 36 | dest_ty.assertHasLayout(zcu); |
| 37 | |
| 38 | var unpack: UnpackValueBytes = .{ |
| 39 | .pt = pt, |
| 40 | .arena = sema.arena, |
| 41 | .skip_bytes = byte_offset, |
| 42 | .remaining_bytes = dest_ty.abiSize(zcu), |
| 43 | .unpacked = .init(sema.arena), |
| 44 | }; |
| 45 | unpack.add(val) catch |err| switch (err) { |
| 46 | error.ReinterpretDeclRef => return null, |
| 47 | error.OutOfMemory => |e| return e, |
| 48 | }; |
| 49 | |
| 50 | var pack: PackValueBytes = .{ |
| 51 | .pt = pt, |
| 52 | .arena = sema.arena, |
| 53 | .unpacked = unpack.unpacked.items, |
| 54 | }; |
| 55 | return pack.get(dest_ty) catch |err| switch (err) { |
| 56 | error.ReinterpretDeclRef => return null, |
| 57 | error.OutOfMemory => |e| return e, |
| 58 | }; |
| 59 | } |
| 60 | |
| 61 | /// Splice the value `splice_val` into `val` at the given `byte_offset`, replacing overlapping bits |
| 62 | /// and returning the modified value. |
| 63 | pub fn spliceMemory( |
| 64 | sema: *Sema, |
| 65 | val: Value, |
| 66 | splice_val: Value, |
| 67 | byte_offset: u64, |
| 68 | ) CompileError!?Value { |
| 69 | const pt = sema.pt; |
| 70 | const zcu = pt.zcu; |
| 71 | const val_ty = val.typeOf(zcu); |
| 72 | const splice_val_ty = splice_val.typeOf(zcu); |
| 73 | |
| 74 | val_ty.assertHasLayout(zcu); |
| 75 | splice_val_ty.assertHasLayout(zcu); |
| 76 | |
| 77 | var unpack: UnpackValueBytes = .{ |
| 78 | .pt = pt, |
| 79 | .arena = sema.arena, |
| 80 | .skip_bytes = 0, |
| 81 | .remaining_bytes = byte_offset, |
| 82 | .unpacked = .init(sema.arena), |
| 83 | }; |
| 84 | unpack.add(val) catch |err| switch (err) { |
| 85 | error.ReinterpretDeclRef => return null, |
| 86 | error.OutOfMemory => |e| return e, |
| 87 | }; |
| 88 | |
| 89 | const splice_len = splice_val_ty.abiSize(zcu); |
| 90 | |
| 91 | unpack.remaining_bytes = splice_len; |
| 92 | unpack.add(splice_val) catch |err| switch (err) { |
| 93 | error.ReinterpretDeclRef => return null, |
| 94 | error.OutOfMemory => |e| return e, |
| 95 | }; |
| 96 | |
| 97 | unpack.skip_bytes = byte_offset + splice_len; |
| 98 | unpack.remaining_bytes = val_ty.abiSize(zcu) * 8 - byte_offset - splice_len; |
| 99 | unpack.add(val) catch |err| switch (err) { |
| 100 | error.ReinterpretDeclRef => return null, |
| 101 | error.OutOfMemory => |e| return e, |
| 102 | }; |
| 103 | |
| 104 | var pack: PackValueBytes = .{ |
| 105 | .pt = pt, |
| 106 | .arena = sema.arena, |
| 107 | .unpacked = unpack.unpacked.items, |
| 108 | }; |
| 109 | return pack.get(val_ty) catch |err| switch (err) { |
| 110 | error.ReinterpretDeclRef => return null, |
| 111 | error.OutOfMemory => |e| return e, |
| 112 | }; |
| 113 | } |
| 114 | |
| 115 | /// Recurses through struct fields, array elements, etc, to get a sequence of "primitive" values |
| 116 | /// which are bit-packed in memory to represent a single value. `unpacked` represents a series |
| 117 | /// of values in *packed* memory - therefore, on big-endian targets, the first element of this |
| 118 | /// list contains bits from the *final* byte of the value. |
| 119 | const UnpackValueBytes = struct { |
| 120 | pt: Zcu.PerThread, |
| 121 | arena: Allocator, |
| 122 | skip_bytes: u64, |
| 123 | remaining_bytes: u64, |
| 124 | unpacked: std.array_list.Managed(InternPool.Index), |
| 125 | |
| 126 | fn add(unpack: *UnpackValueBytes, val: Value) (error{ReinterpretDeclRef} || Allocator.Error)!void { |
| 127 | const pt = unpack.pt; |
| 128 | const zcu = pt.zcu; |
| 129 | const ip = &zcu.intern_pool; |
| 130 | |
| 131 | if (unpack.remaining_bytes == 0) { |
| 132 | return; |
| 133 | } |
| 134 | |
| 135 | const ty = val.typeOf(zcu); |
| 136 | const size = ty.abiSize(zcu); |
| 137 | |
| 138 | if (unpack.skip_bytes >= size) { |
| 139 | unpack.skip_bytes -= size; |
| 140 | return; |
| 141 | } |
| 142 | |
| 143 | switch (ip.indexToKey(val.toIntern())) { |
| 144 | .int_type, |
| 145 | .ptr_type, |
| 146 | .array_type, |
| 147 | .vector_type, |
| 148 | .opt_type, |
| 149 | .anyframe_type, |
| 150 | .error_union_type, |
| 151 | .simple_type, |
| 152 | .struct_type, |
| 153 | .tuple_type, |
| 154 | .union_type, |
| 155 | .opaque_type, |
| 156 | .spirv_type, |
| 157 | .enum_type, |
| 158 | .func_type, |
| 159 | .error_set_type, |
| 160 | .inferred_error_set_type, |
| 161 | .@"extern", |
| 162 | .func, |
| 163 | .err, |
| 164 | .error_union, |
| 165 | .enum_literal, |
| 166 | .slice, |
| 167 | .memoized_call, |
| 168 | => unreachable, // ill-defined layout or not real values |
| 169 | |
| 170 | .undef, |
| 171 | .int, |
| 172 | .enum_tag, |
| 173 | .simple_value, |
| 174 | .float, |
| 175 | .ptr, |
| 176 | .opt, |
| 177 | => try unpack.primitive(val), |
| 178 | |
| 179 | .bitpack => |bitpack| try unpack.primitive(.fromInterned(bitpack.backing_int_val)), |
| 180 | |
| 181 | .aggregate => switch (ty.zigTypeTag(zcu)) { |
| 182 | .vector => unreachable, // ill-defined layout |
| 183 | .array => { |
| 184 | for (0..@intCast(ty.arrayLen(zcu))) |elem_index| { |
| 185 | const elem_val = try val.elemValue(pt, @intCast(elem_index)); |
| 186 | try unpack.add(elem_val); |
| 187 | } |
| 188 | if (ty.sentinel(zcu)) |s| { |
| 189 | try unpack.add(s); |
| 190 | } |
| 191 | }, |
| 192 | .@"struct" => switch (ty.containerLayout(zcu)) { |
| 193 | .auto => unreachable, // ill-defined layout |
| 194 | .@"packed" => unreachable, // uses `.bitpack`, not `.aggregate` |
| 195 | .@"extern" => { |
| 196 | var it = ip.loadStructType(ty.toIntern()).iterateRuntimeOrder(ip); |
| 197 | var offset: u64 = 0; |
| 198 | while (it.next()) |field_index| { |
| 199 | const pad_bytes = ty.structFieldOffset(field_index, zcu) - offset; |
| 200 | const field_val = try val.fieldValue(pt, field_index); |
| 201 | try unpack.padding(pad_bytes); |
| 202 | try unpack.add(field_val); |
| 203 | offset += pad_bytes + field_val.typeOf(zcu).abiSize(zcu); |
| 204 | } |
| 205 | try unpack.padding(size - offset); |
| 206 | }, |
| 207 | }, |
| 208 | else => unreachable, |
| 209 | }, |
| 210 | |
| 211 | .un => |un| { |
| 212 | const payload_val = Value.fromInterned(un.val); |
| 213 | const pad_bytes = size - payload_val.typeOf(zcu).abiSize(zcu); |
| 214 | try unpack.add(payload_val); |
| 215 | try unpack.padding(pad_bytes); |
| 216 | }, |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | fn padding(unpack: *UnpackValueBytes, num_bytes: u64) Allocator.Error!void { |
| 221 | if (num_bytes == 0) return; |
| 222 | const undef_u8 = try unpack.pt.undefValue(Type.u8); |
| 223 | for (0..@intCast(num_bytes)) |_| { |
| 224 | unpack.primitive(undef_u8) catch |err| switch (err) { |
| 225 | error.OutOfMemory => |e| return e, |
| 226 | error.ReinterpretDeclRef => unreachable, |
| 227 | }; |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | fn primitive(unpack: *UnpackValueBytes, val: Value) (error{ReinterpretDeclRef} || Allocator.Error)!void { |
| 232 | const pt = unpack.pt; |
| 233 | const zcu = pt.zcu; |
| 234 | |
| 235 | if (unpack.remaining_bytes == 0) { |
| 236 | return; |
| 237 | } |
| 238 | |
| 239 | const ty = val.typeOf(pt.zcu); |
| 240 | const size = ty.abiSize(zcu); |
| 241 | |
| 242 | if (unpack.skip_bytes >= size) { |
| 243 | unpack.skip_bytes -= size; |
| 244 | return; |
| 245 | } |
| 246 | |
| 247 | if (unpack.skip_bytes > 0) { |
| 248 | const offset = unpack.skip_bytes; |
| 249 | unpack.skip_bytes = 0; |
| 250 | return unpack.splitPrimitive(val, offset, @min(size - offset, unpack.remaining_bytes)); |
| 251 | } |
| 252 | |
| 253 | if (unpack.remaining_bytes < size) { |
| 254 | return unpack.splitPrimitive(val, 0, unpack.remaining_bytes); |
| 255 | } |
| 256 | |
| 257 | unpack.remaining_bytes -= size; |
| 258 | try unpack.unpacked.append(val.toIntern()); |
| 259 | } |
| 260 | |
| 261 | fn splitPrimitive(unpack: *UnpackValueBytes, val: Value, offset: u64, len: u64) (error{ReinterpretDeclRef} || Allocator.Error)!void { |
| 262 | const pt = unpack.pt; |
| 263 | const zcu = pt.zcu; |
| 264 | const ty = val.typeOf(pt.zcu); |
| 265 | |
| 266 | assert(offset + len <= ty.abiSize(zcu)); |
| 267 | |
| 268 | try unpack.unpacked.ensureUnusedCapacity(@intCast(len)); |
| 269 | unpack.remaining_bytes -= len; |
| 270 | |
| 271 | switch (pt.zcu.intern_pool.indexToKey(val.toIntern())) { |
| 272 | // In the `ptr` case, this will return `error.ReinterpretDeclRef` |
| 273 | // if we're trying to split a non-integer pointer value. |
| 274 | .int, .float, .enum_tag, .ptr, .opt => { |
| 275 | const buf = try unpack.arena.alloc(u8, @intCast(ty.abiSize(zcu))); |
| 276 | val.writeToMemory(zcu, buf) catch |err| switch (err) { |
| 277 | error.IllDefinedMemoryLayout => unreachable, |
| 278 | else => |e| return e, |
| 279 | }; |
| 280 | for (buf[@intCast(offset)..][0..@intCast(len)]) |byte_raw| { |
| 281 | const byte_val = try pt.intValue(.u8, byte_raw); |
| 282 | unpack.unpacked.appendAssumeCapacity(byte_val.toIntern()); |
| 283 | } |
| 284 | }, |
| 285 | .undef => { |
| 286 | const undef_u8 = try pt.undefValue(.u8); |
| 287 | for (0..@intCast(len)) |_| { |
| 288 | unpack.unpacked.appendAssumeCapacity(undef_u8.toIntern()); |
| 289 | } |
| 290 | }, |
| 291 | // The only values here with runtime bits are `true` and `false`. |
| 292 | // These are both 1 byte, so will never need splitting. |
| 293 | .simple_value => unreachable, |
| 294 | else => unreachable, // zero-bit or not primitives |
| 295 | } |
| 296 | } |
| 297 | }; |
| 298 | |
| 299 | /// Given a sequence of bit-packed values in packed memory (see `UnpackValueBytes`), |
| 300 | /// reconstructs a value of an arbitrary type, with correct handling of `undefined` |
| 301 | /// values and of pointers which align in virtual memory. |
| 302 | const PackValueBytes = struct { |
| 303 | pt: Zcu.PerThread, |
| 304 | arena: Allocator, |
| 305 | byte_offset: u64 = 0, |
| 306 | unpacked: []const InternPool.Index, |
| 307 | |
| 308 | fn get(pack: *PackValueBytes, ty: Type) (Allocator.Error || error{ReinterpretDeclRef})!Value { |
| 309 | const pt = pack.pt; |
| 310 | const zcu = pt.zcu; |
| 311 | const ip = &zcu.intern_pool; |
| 312 | const arena = pack.arena; |
| 313 | switch (ty.zigTypeTag(zcu)) { |
| 314 | .vector => unreachable, // ill-defined layout |
| 315 | .array => { |
| 316 | // Each element is padded up to its ABI size. The final element does not have trailing padding. |
| 317 | const elem_ty = ty.childType(zcu); |
| 318 | const elems = try arena.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu))); |
| 319 | |
| 320 | for (elems) |*elem| { |
| 321 | elem.* = (try pack.get(elem_ty)).toIntern(); |
| 322 | } |
| 323 | |
| 324 | if (ty.sentinel(zcu)) |s| { |
| 325 | _ = s; // TODO: validate sentinel was preserved! |
| 326 | pack.padding(elem_ty.abiSize(zcu)); |
| 327 | } |
| 328 | |
| 329 | return pt.aggregateValue(ty, elems); |
| 330 | }, |
| 331 | .@"struct" => switch (ty.containerLayout(zcu)) { |
| 332 | .auto => unreachable, // ill-defined layout |
| 333 | .@"extern" => { |
| 334 | const elems = try arena.alloc(InternPool.Index, ty.structFieldCount(zcu)); |
| 335 | @memset(elems, .none); |
| 336 | var offset: u64 = 0; |
| 337 | var it = ip.loadStructType(ty.toIntern()).iterateRuntimeOrder(ip); |
| 338 | while (it.next()) |field_index| { |
| 339 | const field_ty = ty.fieldType(field_index, zcu); |
| 340 | const pad_bytes = ty.structFieldOffset(field_index, zcu) - offset; |
| 341 | pack.padding(pad_bytes); |
| 342 | elems[field_index] = (try pack.get(field_ty)).toIntern(); |
| 343 | offset += pad_bytes + field_ty.abiSize(zcu); |
| 344 | } |
| 345 | pack.padding(ty.abiSize(zcu) - offset); |
| 346 | // Any fields which do not have runtime bits should be OPV or comptime fields. |
| 347 | // Fill those values now. |
| 348 | for (elems, 0..) |*elem, field_index| { |
| 349 | if (elem.* != .none) continue; |
| 350 | const val = (try ty.structFieldValueComptime(pt, field_index)).?; |
| 351 | elem.* = val.toIntern(); |
| 352 | } |
| 353 | return pt.aggregateValue(ty, elems); |
| 354 | }, |
| 355 | .@"packed" => { |
| 356 | const backing_int_val = try pack.primitive(ty.backingIntType(zcu)); |
| 357 | if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty); |
| 358 | return pt.bitpackValue(ty, backing_int_val); |
| 359 | }, |
| 360 | }, |
| 361 | .@"union" => switch (ty.containerLayout(zcu)) { |
| 362 | .auto => unreachable, // ill-defined layout |
| 363 | .@"extern" => { |
| 364 | // We will attempt to read as the backing representation. If this emits |
| 365 | // `error.ReinterpretDeclRef`, we will try each union field, preferring larger ones. |
| 366 | // We will also attempt smaller fields when we get `undefined`, as if some bits are |
| 367 | // defined we want to include them. |
| 368 | // TODO: this is very very bad. We need a more sophisticated union representation. |
| 369 | |
| 370 | const prev_unpacked = pack.unpacked; |
| 371 | const prev_byte_offset = pack.byte_offset; |
| 372 | |
| 373 | const backing_ty = try ty.externUnionBackingType(pt); |
| 374 | |
| 375 | const backing_result: enum { undef, reinterpret_decl_ref } = backing: { |
| 376 | const backing_val = pack.get(backing_ty) catch |err| switch (err) { |
| 377 | error.ReinterpretDeclRef => break :backing .reinterpret_decl_ref, |
| 378 | else => |e| return e, |
| 379 | }; |
| 380 | if (backing_val.isUndef(zcu)) break :backing .undef; |
| 381 | return .fromInterned(try pt.internUnion(.{ |
| 382 | .ty = ty.toIntern(), |
| 383 | .tag = .none, |
| 384 | .val = backing_val.toIntern(), |
| 385 | })); |
| 386 | }; |
| 387 | |
| 388 | const field_order = try pack.arena.alloc(u32, ty.unionTagTypeHypothetical(zcu).enumFieldCount(zcu)); |
| 389 | for (field_order, 0..) |*f, i| f.* = @intCast(i); |
| 390 | // Sort `field_order` to put the fields with the largest ABI sizes first. |
| 391 | const SizeSortCtx = struct { |
| 392 | zcu: *const Zcu, |
| 393 | field_types: []const InternPool.Index, |
| 394 | fn lessThan(ctx: @This(), a_idx: u32, b_idx: u32) bool { |
| 395 | const a_ty: Type = .fromInterned(ctx.field_types[a_idx]); |
| 396 | const b_ty: Type = .fromInterned(ctx.field_types[b_idx]); |
| 397 | return a_ty.abiSize(ctx.zcu) > b_ty.abiSize(ctx.zcu); |
| 398 | } |
| 399 | }; |
| 400 | std.mem.sortUnstable(u32, field_order, SizeSortCtx{ |
| 401 | .zcu = zcu, |
| 402 | .field_types = zcu.typeToUnion(ty).?.field_types.get(ip), |
| 403 | }, SizeSortCtx.lessThan); |
| 404 | |
| 405 | for (field_order) |field_index| { |
| 406 | pack.unpacked = prev_unpacked; |
| 407 | pack.byte_offset = prev_byte_offset; |
| 408 | const field_ty = ty.fieldType(field_index, zcu); |
| 409 | const field_val = pack.get(field_ty) catch |err| switch (err) { |
| 410 | error.ReinterpretDeclRef => continue, |
| 411 | else => |e| return e, |
| 412 | }; |
| 413 | if (field_val.isUndef(zcu)) continue; |
| 414 | pack.padding(ty.abiSize(zcu) - field_ty.abiSize(zcu)); |
| 415 | const tag_val = try pt.enumValueFieldIndex(ty.unionTagTypeHypothetical(zcu), field_index); |
| 416 | return pt.unionValue(ty, tag_val, field_val); |
| 417 | } |
| 418 | |
| 419 | // No field could represent the value. Just do whatever happens when we try to read |
| 420 | // the backing type - either `undefined` or `error.ReinterpretDeclRef`. |
| 421 | switch (backing_result) { |
| 422 | .undef => return pt.undefValue(ty), |
| 423 | .reinterpret_decl_ref => return error.ReinterpretDeclRef, |
| 424 | } |
| 425 | }, |
| 426 | .@"packed" => { |
| 427 | const backing_int_val = try pack.primitive(ty.backingIntType(zcu)); |
| 428 | if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty); |
| 429 | return pt.bitpackValue(ty, backing_int_val); |
| 430 | }, |
| 431 | }, |
| 432 | .@"enum" => { |
| 433 | const tag_int_val = try pack.primitive(ty.backingIntType(zcu)); |
| 434 | if (tag_int_val.isUndef(zcu)) return pt.undefValue(ty); |
| 435 | return pt.enumValue(ty, tag_int_val); |
| 436 | }, |
| 437 | else => return pack.primitive(ty), |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | fn padding(pack: *PackValueBytes, num_bytes: u64) void { |
| 442 | _ = pack.prepareBytes(num_bytes); |
| 443 | } |
| 444 | |
| 445 | fn primitive(pack: *PackValueBytes, want_ty: Type) (Allocator.Error || error{ReinterpretDeclRef})!Value { |
| 446 | const pt = pack.pt; |
| 447 | const zcu = pt.zcu; |
| 448 | |
| 449 | if (try want_ty.onePossibleValue(pt)) |opv| return opv; |
| 450 | |
| 451 | const vals, const byte_offset = pack.prepareBytes(want_ty.abiSize(zcu)); |
| 452 | |
| 453 | for (vals) |val| { |
| 454 | if (!Value.fromInterned(val).isUndef(zcu)) break; |
| 455 | } else { |
| 456 | // All bits of the value are `undefined`. |
| 457 | return pt.undefValue(want_ty); |
| 458 | } |
| 459 | |
| 460 | // TODO: we need to decide how to handle partially-undef values here. |
| 461 | // Currently, a value with some undefined bits becomes `0xAA` so that we |
| 462 | // preserve the well-defined bits, because we can't currently represent |
| 463 | // a partially-undefined primitive (e.g. an int with some undef bits). |
| 464 | // In future, we probably want to take one of these two routes: |
| 465 | // * Define that if any bits are `undefined`, the entire value is `undefined`. |
| 466 | // This is a major breaking change, and probably a footgun. |
| 467 | // * Introduce tracking for partially-undef values at comptime. |
| 468 | // This would complicate a lot of operations in Sema, such as basic |
| 469 | // arithmetic. |
| 470 | // This design complexity is tracked by #19634. |
| 471 | |
| 472 | if (vals.len == 1 and |
| 473 | want_ty.isPtrAtRuntime(zcu) and |
| 474 | Value.fromInterned(vals[0]).typeOf(zcu).isPtrAtRuntime(zcu)) |
| 475 | { |
| 476 | return pt.getCoerced(.fromInterned(vals[0]), want_ty); |
| 477 | } |
| 478 | |
| 479 | // Reinterpret via an in-memory buffer. |
| 480 | |
| 481 | var buf_len: u64 = 0; |
| 482 | for (vals) |ip_val| { |
| 483 | const val: Value = .fromInterned(ip_val); |
| 484 | buf_len += val.typeOf(zcu).abiSize(zcu); |
| 485 | } |
| 486 | |
| 487 | const buf = try pack.arena.alloc(u8, @intCast(buf_len)); |
| 488 | { |
| 489 | var offset: usize = 0; |
| 490 | for (vals) |ip_val| { |
| 491 | const val: Value = .fromInterned(ip_val); |
| 492 | const ty = val.typeOf(zcu); |
| 493 | const size = ty.abiSize(zcu); |
| 494 | if (val.isUndef(zcu)) { |
| 495 | @memset(buf[offset..][0..@intCast(size)], 0xAA); |
| 496 | } else { |
| 497 | val.writeToMemory(zcu, buf[offset..][0..@intCast(size)]) catch |err| switch (err) { |
| 498 | error.IllDefinedMemoryLayout => unreachable, |
| 499 | else => |e| return e, |
| 500 | }; |
| 501 | } |
| 502 | offset += @intCast(size); |
| 503 | } |
| 504 | } |
| 505 | const bytes = buf[@intCast(byte_offset)..]; |
| 506 | |
| 507 | const target = zcu.getTarget(); |
| 508 | const endian = target.cpu.arch.endian(); |
| 509 | switch (want_ty.zigTypeTag(zcu)) { |
| 510 | .bool => return .makeBool(bytes[0] != 0), |
| 511 | .int => return .readIntFromMemory(want_ty, pt, bytes, pack.arena), |
| 512 | .float => switch (want_ty.floatBits(target)) { |
| 513 | 16 => return pt.floatValue(want_ty, @as(f16, @bitCast(std.mem.readInt(u16, bytes[0..2], endian)))), |
| 514 | 32 => return pt.floatValue(want_ty, @as(f32, @bitCast(std.mem.readInt(u32, bytes[0..4], endian)))), |
| 515 | 64 => return pt.floatValue(want_ty, @as(f64, @bitCast(std.mem.readInt(u64, bytes[0..8], endian)))), |
| 516 | 80 => return pt.floatValue(want_ty, @as(f80, @bitCast(std.mem.readInt(u80, bytes[0..10], endian)))), |
| 517 | 128 => return pt.floatValue(want_ty, @as(f128, @bitCast(std.mem.readInt(u128, bytes[0..16], endian)))), |
| 518 | else => unreachable, |
| 519 | }, |
| 520 | .pointer => { |
| 521 | assert(!want_ty.isSlice(zcu)); |
| 522 | const ptr_addr = std.mem.readVarInt(u64, bytes[0..@intCast(want_ty.abiSize(zcu))], endian); |
| 523 | return pt.ptrIntValue(want_ty, ptr_addr); |
| 524 | }, |
| 525 | .optional => { |
| 526 | assert(want_ty.isPtrLikeOptional(zcu)); |
| 527 | const ptr_ty = want_ty.optionalChild(zcu); |
| 528 | const ptr_addr = std.mem.readVarInt(u64, bytes[0..@intCast(want_ty.abiSize(zcu))], endian); |
| 529 | return .fromInterned(try pt.intern(.{ .opt = .{ |
| 530 | .ty = want_ty.toIntern(), |
| 531 | .val = if (ptr_addr == 0) .none else (try pt.ptrIntValue(ptr_ty, ptr_addr)).toIntern(), |
| 532 | } })); |
| 533 | }, |
| 534 | else => unreachable, |
| 535 | } |
| 536 | } |
| 537 | |
| 538 | fn prepareBytes(pack: *PackValueBytes, need_bytes: u64) struct { []const InternPool.Index, u64 } { |
| 539 | if (need_bytes == 0) return .{ &.{}, 0 }; |
| 540 | |
| 541 | const pt = pack.pt; |
| 542 | const zcu = pt.zcu; |
| 543 | |
| 544 | var bytes: u64 = 0; |
| 545 | var len: usize = 0; |
| 546 | while (bytes < pack.byte_offset + need_bytes) { |
| 547 | bytes += Value.fromInterned(pack.unpacked[len]).typeOf(zcu).abiSize(zcu); |
| 548 | len += 1; |
| 549 | } |
| 550 | |
| 551 | const result_vals = pack.unpacked[0..len]; |
| 552 | const result_offset = pack.byte_offset; |
| 553 | |
| 554 | const extra_bytes = bytes - pack.byte_offset - need_bytes; |
| 555 | if (extra_bytes == 0) { |
| 556 | pack.unpacked = pack.unpacked[len..]; |
| 557 | pack.byte_offset = 0; |
| 558 | } else { |
| 559 | pack.unpacked = pack.unpacked[len - 1 ..]; |
| 560 | pack.byte_offset = Value.fromInterned(pack.unpacked[0]).typeOf(zcu).abiSize(zcu) - extra_bytes; |
| 561 | } |
| 562 | |
| 563 | return .{ result_vals, result_offset }; |
| 564 | } |
| 565 | }; |
| 566 | |
| 567 | const std = @import("std"); |
| 568 | const Allocator = std.mem.Allocator; |
| 569 | const assert = std.debug.assert; |
| 570 | |
| 571 | const Sema = @import("../Sema.zig"); |
| 572 | const Zcu = @import("../Zcu.zig"); |
| 573 | const InternPool = @import("../InternPool.zig"); |
| 574 | const Type = @import("../Type.zig"); |
| 575 | const Value = @import("../Value.zig"); |
| 576 | const CompileError = Zcu.CompileError; |