blob ed04ee47 (254316B) - Raw
1 const std = @import("std"); 2 const Allocator = std.mem.Allocator; 3 const Target = std.Target; 4 const log = std.log.scoped(.codegen); 5 const assert = std.debug.assert; 6 7 const Module = @import("../Module.zig"); 8 const Decl = Module.Decl; 9 const Type = @import("../type.zig").Type; 10 const Value = @import("../Value.zig"); 11 const LazySrcLoc = std.zig.LazySrcLoc; 12 const Air = @import("../Air.zig"); 13 const Liveness = @import("../Liveness.zig"); 14 const InternPool = @import("../InternPool.zig"); 15 16 const spec = @import("spirv/spec.zig"); 17 const Opcode = spec.Opcode; 18 const Word = spec.Word; 19 const IdRef = spec.IdRef; 20 const IdResult = spec.IdResult; 21 const IdResultType = spec.IdResultType; 22 const StorageClass = spec.StorageClass; 23 24 const SpvModule = @import("spirv/Module.zig"); 25 26 const SpvSection = @import("spirv/Section.zig"); 27 const SpvAssembler = @import("spirv/Assembler.zig"); 28 29 const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef); 30 31 pub const zig_call_abi_ver = 3; 32 33 const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, DeclGen.Repr }, IdResult); 34 const PtrTypeMap = std.AutoHashMapUnmanaged( 35 struct { InternPool.Index, StorageClass }, 36 struct { ty_id: IdRef, fwd_emitted: bool }, 37 ); 38 39 const ControlFlow = union(enum) { 40 const Structured = struct { 41 /// This type indicates the way that a block is terminated. The 42 /// state of a particular block is used to track how a jump from 43 /// inside the block must reach the outside. 44 const Block = union(enum) { 45 const Incoming = struct { 46 src_label: IdRef, 47 /// Instruction that returns an u32 value of the 48 /// `Air.Inst.Index` that control flow should jump to. 49 next_block: IdRef, 50 }; 51 52 const SelectionMerge = struct { 53 /// Incoming block from the `then` label. 54 /// Note that hte incoming block from the `else` label is 55 /// either given by the next element in the stack. 56 incoming: Incoming, 57 /// The label id of the cond_br's merge block. 58 /// For the top-most element in the stack, this 59 /// value is undefined. 60 merge_block: IdRef, 61 }; 62 63 /// For a `selection` type block, we cannot use early exits, and we 64 /// must generate a 'merge ladder' of OpSelection instructions. To that end, 65 /// we keep a stack of the merges that still must be closed at the end of 66 /// a block. 67 /// 68 /// This entire structure basically just resembles a tree like 69 /// a x 70 /// \ / 71 /// b o merge 72 /// \ / 73 /// c o merge 74 /// \ / 75 /// o merge 76 /// / 77 /// o jump to next block 78 selection: struct { 79 /// In order to know which merges we still need to do, we need to keep 80 /// a stack of those. 81 merge_stack: std.ArrayListUnmanaged(SelectionMerge) = .{}, 82 }, 83 /// For a `loop` type block, we can early-exit the block by 84 /// jumping to the loop exit node, and we don't need to generate 85 /// an entire stack of merges. 86 loop: struct { 87 /// The next block to jump to can be determined from any number 88 /// of conditions that jump to the loop exit. 89 merges: std.ArrayListUnmanaged(Incoming) = .{}, 90 /// The label id of the loop's merge block. 91 merge_block: IdRef, 92 }, 93 94 fn deinit(self: *Structured.Block, a: Allocator) void { 95 switch (self.*) { 96 .selection => |*merge| merge.merge_stack.deinit(a), 97 .loop => |*merge| merge.merges.deinit(a), 98 } 99 self.* = undefined; 100 } 101 }; 102 /// The stack of (structured) blocks that we are currently in. This determines 103 /// how exits from the current block must be handled. 104 block_stack: std.ArrayListUnmanaged(*Structured.Block) = .{}, 105 /// Maps `block` inst indices to the variable that the block's result 106 /// value must be written to. 107 block_results: std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef) = .{}, 108 }; 109 110 const Unstructured = struct { 111 const Incoming = struct { 112 src_label: IdRef, 113 break_value_id: IdRef, 114 }; 115 116 const Block = struct { 117 label: ?IdRef = null, 118 incoming_blocks: std.ArrayListUnmanaged(Incoming) = .{}, 119 }; 120 121 /// We need to keep track of result ids for block labels, as well as the 'incoming' 122 /// blocks for a block. 123 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *Block) = .{}, 124 }; 125 126 structured: Structured, 127 unstructured: Unstructured, 128 129 pub fn deinit(self: *ControlFlow, a: Allocator) void { 130 switch (self.*) { 131 .structured => |*cf| { 132 cf.block_stack.deinit(a); 133 cf.block_results.deinit(a); 134 }, 135 .unstructured => |*cf| { 136 cf.blocks.deinit(a); 137 }, 138 } 139 self.* = undefined; 140 } 141 }; 142 143 /// This structure holds information that is relevant to the entire compilation, 144 /// in contrast to `DeclGen`, which only holds relevant information about a 145 /// single decl. 146 pub const Object = struct { 147 /// A general-purpose allocator that can be used for any allocation for this Object. 148 gpa: Allocator, 149 150 /// the SPIR-V module that represents the final binary. 151 spv: SpvModule, 152 153 /// The Zig module that this object file is generated for. 154 /// A map of Zig decl indices to SPIR-V decl indices. 155 decl_link: std.AutoHashMapUnmanaged(InternPool.DeclIndex, SpvModule.Decl.Index) = .{}, 156 157 /// A map of Zig InternPool indices for anonymous decls to SPIR-V decl indices. 158 anon_decl_link: std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index) = .{}, 159 160 /// A map that maps AIR intern pool indices to SPIR-V result-ids. 161 intern_map: InternMap = .{}, 162 163 /// This map serves a dual purpose: 164 /// - It keeps track of pointers that are currently being emitted, so that we can tell 165 /// if they are recursive and need an OpTypeForwardPointer. 166 /// - It caches pointers by child-type. This is required because sometimes we rely on 167 /// ID-equality for pointers, and pointers constructed via `ptrType()` aren't interned 168 /// via the usual `intern_map` mechanism. 169 ptr_types: PtrTypeMap = .{}, 170 171 pub fn init(gpa: Allocator) Object { 172 return .{ 173 .gpa = gpa, 174 .spv = SpvModule.init(gpa), 175 }; 176 } 177 178 pub fn deinit(self: *Object) void { 179 self.spv.deinit(); 180 self.decl_link.deinit(self.gpa); 181 self.anon_decl_link.deinit(self.gpa); 182 self.intern_map.deinit(self.gpa); 183 self.ptr_types.deinit(self.gpa); 184 } 185 186 fn genDecl( 187 self: *Object, 188 mod: *Module, 189 decl_index: InternPool.DeclIndex, 190 air: Air, 191 liveness: Liveness, 192 ) !void { 193 const decl = mod.declPtr(decl_index); 194 const namespace = mod.namespacePtr(decl.src_namespace); 195 const structured_cfg = namespace.file_scope.mod.structured_cfg; 196 197 var decl_gen = DeclGen{ 198 .gpa = self.gpa, 199 .object = self, 200 .module = mod, 201 .spv = &self.spv, 202 .decl_index = decl_index, 203 .air = air, 204 .liveness = liveness, 205 .intern_map = &self.intern_map, 206 .ptr_types = &self.ptr_types, 207 .control_flow = switch (structured_cfg) { 208 true => .{ .structured = .{} }, 209 false => .{ .unstructured = .{} }, 210 }, 211 .current_block_label = undefined, 212 .base_line = decl.src_line, 213 }; 214 defer decl_gen.deinit(); 215 216 decl_gen.genDecl() catch |err| switch (err) { 217 error.CodegenFail => { 218 try mod.failed_decls.put(mod.gpa, decl_index, decl_gen.error_msg.?); 219 }, 220 else => |other| { 221 // There might be an error that happened *after* self.error_msg 222 // was already allocated, so be sure to free it. 223 if (decl_gen.error_msg) |error_msg| { 224 error_msg.deinit(mod.gpa); 225 } 226 227 return other; 228 }, 229 }; 230 } 231 232 pub fn updateFunc( 233 self: *Object, 234 mod: *Module, 235 func_index: InternPool.Index, 236 air: Air, 237 liveness: Liveness, 238 ) !void { 239 const decl_index = mod.funcInfo(func_index).owner_decl; 240 // TODO: Separate types for generating decls and functions? 241 try self.genDecl(mod, decl_index, air, liveness); 242 } 243 244 pub fn updateDecl( 245 self: *Object, 246 mod: *Module, 247 decl_index: InternPool.DeclIndex, 248 ) !void { 249 try self.genDecl(mod, decl_index, undefined, undefined); 250 } 251 252 /// Fetch or allocate a result id for decl index. This function also marks the decl as alive. 253 /// Note: Function does not actually generate the decl, it just allocates an index. 254 pub fn resolveDecl(self: *Object, mod: *Module, decl_index: InternPool.DeclIndex) !SpvModule.Decl.Index { 255 const decl = mod.declPtr(decl_index); 256 assert(decl.has_tv); // TODO: Do we need to handle a situation where this is false? 257 258 const entry = try self.decl_link.getOrPut(self.gpa, decl_index); 259 if (!entry.found_existing) { 260 // TODO: Extern fn? 261 const kind: SpvModule.Decl.Kind = if (decl.val.isFuncBody(mod)) 262 .func 263 else switch (decl.@"addrspace") { 264 .generic => .invocation_global, 265 else => .global, 266 }; 267 268 entry.value_ptr.* = try self.spv.allocDecl(kind); 269 } 270 271 return entry.value_ptr.*; 272 } 273 }; 274 275 /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that. 276 const DeclGen = struct { 277 /// A general-purpose allocator that can be used for any allocations for this DeclGen. 278 gpa: Allocator, 279 280 /// The object that this decl is generated into. 281 object: *Object, 282 283 /// The Zig module that we are generating decls for. 284 module: *Module, 285 286 /// The SPIR-V module that instructions should be emitted into. 287 /// This is the same as `self.object.spv`, repeated here for brevity. 288 spv: *SpvModule, 289 290 /// The decl we are currently generating code for. 291 decl_index: InternPool.DeclIndex, 292 293 /// The intermediate code of the declaration we are currently generating. Note: If 294 /// the declaration is not a function, this value will be undefined! 295 air: Air, 296 297 /// The liveness analysis of the intermediate code for the declaration we are currently generating. 298 /// Note: If the declaration is not a function, this value will be undefined! 299 liveness: Liveness, 300 301 /// An array of function argument result-ids. Each index corresponds with the 302 /// function argument of the same index. 303 args: std.ArrayListUnmanaged(IdRef) = .{}, 304 305 /// A counter to keep track of how many `arg` instructions we've seen yet. 306 next_arg_index: u32 = 0, 307 308 /// A map keeping track of which instruction generated which result-id. 309 inst_results: InstMap = .{}, 310 311 /// A map that maps AIR intern pool indices to SPIR-V result-ids. 312 /// See `Object.intern_map`. 313 intern_map: *InternMap, 314 315 /// Module's pointer types, see `Object.ptr_types`. 316 ptr_types: *PtrTypeMap, 317 318 /// This field keeps track of the current state wrt structured or unstructured control flow. 319 control_flow: ControlFlow, 320 321 /// The label of the SPIR-V block we are currently generating. 322 current_block_label: IdRef, 323 324 /// The code (prologue and body) for the function we are currently generating code for. 325 func: SpvModule.Fn = .{}, 326 327 /// The base offset of the current decl, which is what `dbg_stmt` is relative to. 328 base_line: u32, 329 330 /// If `gen` returned `Error.CodegenFail`, this contains an explanatory message. 331 /// Memory is owned by `module.gpa`. 332 error_msg: ?*Module.ErrorMsg = null, 333 334 /// Possible errors the `genDecl` function may return. 335 const Error = error{ CodegenFail, OutOfMemory }; 336 337 /// This structure is used to return information about a type typically used for 338 /// arithmetic operations. These types may either be integers, floats, or a vector 339 /// of these. Most scalar operations also work on vectors, so we can easily represent 340 /// those as arithmetic types. If the type is a scalar, 'inner type' refers to the 341 /// scalar type. Otherwise, if its a vector, it refers to the vector's element type. 342 const ArithmeticTypeInfo = struct { 343 /// A classification of the inner type. 344 const Class = enum { 345 /// A boolean. 346 bool, 347 348 /// A regular, **native**, integer. 349 /// This is only returned when the backend supports this int as a native type (when 350 /// the relevant capability is enabled). 351 integer, 352 353 /// A regular float. These are all required to be natively supported. Floating points 354 /// for which the relevant capability is not enabled are not emulated. 355 float, 356 357 /// An integer of a 'strange' size (which' bit size is not the same as its backing 358 /// type. **Note**: this may **also** include power-of-2 integers for which the 359 /// relevant capability is not enabled), but still within the limits of the largest 360 /// natively supported integer type. 361 strange_integer, 362 363 /// An integer with more bits than the largest natively supported integer type. 364 composite_integer, 365 }; 366 367 /// The number of bits in the inner type. 368 /// This is the actual number of bits of the type, not the size of the backing integer. 369 bits: u16, 370 371 /// The number of bits required to store the type. 372 /// For `integer` and `float`, this is equal to `bits`. 373 /// For `strange_integer` and `bool` this is the size of the backing integer. 374 /// For `composite_integer` this is 0 (TODO) 375 backing_bits: u16, 376 377 /// Null if this type is a scalar, or the length 378 /// of the vector otherwise. 379 vector_len: ?u32, 380 381 /// Whether the inner type is signed. Only relevant for integers. 382 signedness: std.builtin.Signedness, 383 384 /// A classification of the inner type. These scenarios 385 /// will all have to be handled slightly different. 386 class: Class, 387 }; 388 389 /// Data can be lowered into in two basic representations: indirect, which is when 390 /// a type is stored in memory, and direct, which is how a type is stored when its 391 /// a direct SPIR-V value. 392 const Repr = enum { 393 /// A SPIR-V value as it would be used in operations. 394 direct, 395 /// A SPIR-V value as it is stored in memory. 396 indirect, 397 }; 398 399 /// Free resources owned by the DeclGen. 400 pub fn deinit(self: *DeclGen) void { 401 self.args.deinit(self.gpa); 402 self.inst_results.deinit(self.gpa); 403 self.control_flow.deinit(self.gpa); 404 self.func.deinit(self.gpa); 405 } 406 407 /// Return the target which we are currently compiling for. 408 pub fn getTarget(self: *DeclGen) std.Target { 409 return self.module.getTarget(); 410 } 411 412 pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error { 413 @setCold(true); 414 const mod = self.module; 415 const src_loc = self.module.declPtr(self.decl_index).srcLoc(mod); 416 assert(self.error_msg == null); 417 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); 418 return error.CodegenFail; 419 } 420 421 pub fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) Error { 422 return self.fail("TODO (SPIR-V): " ++ format, args); 423 } 424 425 /// Fetch the result-id for a previously generated instruction or constant. 426 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !IdRef { 427 const mod = self.module; 428 if (try self.air.value(inst, mod)) |val| { 429 const ty = self.typeOf(inst); 430 if (ty.zigTypeTag(mod) == .Fn) { 431 const fn_decl_index = switch (mod.intern_pool.indexToKey(val.ip_index)) { 432 .extern_func => |extern_func| extern_func.decl, 433 .func => |func| func.owner_decl, 434 else => unreachable, 435 }; 436 const spv_decl_index = try self.object.resolveDecl(mod, fn_decl_index); 437 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); 438 return self.spv.declPtr(spv_decl_index).result_id; 439 } 440 441 return try self.constant(ty, val, .direct); 442 } 443 const index = inst.toIndex().?; 444 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage. 445 } 446 447 fn resolveAnonDecl(self: *DeclGen, val: InternPool.Index) !IdRef { 448 // TODO: This cannot be a function at this point, but it should probably be handled anyway. 449 450 const mod = self.module; 451 const ty = Type.fromInterned(mod.intern_pool.typeOf(val)); 452 const decl_ptr_ty_id = try self.ptrType(ty, .Generic); 453 454 const spv_decl_index = blk: { 455 const entry = try self.object.anon_decl_link.getOrPut(self.object.gpa, .{ val, .Function }); 456 if (entry.found_existing) { 457 try self.addFunctionDep(entry.value_ptr.*, .Function); 458 459 const result_id = self.spv.declPtr(entry.value_ptr.*).result_id; 460 return try self.castToGeneric(decl_ptr_ty_id, result_id); 461 } 462 463 const spv_decl_index = try self.spv.allocDecl(.invocation_global); 464 try self.addFunctionDep(spv_decl_index, .Function); 465 entry.value_ptr.* = spv_decl_index; 466 break :blk spv_decl_index; 467 }; 468 469 // TODO: At some point we will be able to generate this all constant here, but then all of 470 // constant() will need to be implemented such that it doesn't generate any at-runtime code. 471 // NOTE: Because this is a global, we really only want to initialize it once. Therefore the 472 // constant lowering of this value will need to be deferred to an initializer similar to 473 // other globals. 474 475 const result_id = self.spv.declPtr(spv_decl_index).result_id; 476 477 { 478 // Save the current state so that we can temporarily generate into a different function. 479 // TODO: This should probably be made a little more robust. 480 const func = self.func; 481 defer self.func = func; 482 const block_label = self.current_block_label; 483 defer self.current_block_label = block_label; 484 485 self.func = .{}; 486 defer self.func.deinit(self.gpa); 487 488 const initializer_proto_ty_id = try self.functionType(Type.void, &.{}); 489 490 const initializer_id = self.spv.allocId(); 491 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ 492 .id_result_type = try self.resolveType(Type.void, .direct), 493 .id_result = initializer_id, 494 .function_control = .{}, 495 .function_type = initializer_proto_ty_id, 496 }); 497 const root_block_id = self.spv.allocId(); 498 try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{ 499 .id_result = root_block_id, 500 }); 501 self.current_block_label = root_block_id; 502 503 const val_id = try self.constant(ty, Value.fromInterned(val), .indirect); 504 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 505 .pointer = result_id, 506 .object = val_id, 507 }); 508 509 try self.func.body.emit(self.spv.gpa, .OpReturn, {}); 510 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); 511 try self.spv.addFunction(spv_decl_index, self.func); 512 513 try self.spv.debugNameFmt(initializer_id, "initializer of __anon_{d}", .{@intFromEnum(val)}); 514 515 const fn_decl_ptr_ty_id = try self.ptrType(ty, .Function); 516 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpExtInst, .{ 517 .id_result_type = fn_decl_ptr_ty_id, 518 .id_result = result_id, 519 .set = try self.spv.importInstructionSet(.zig), 520 .instruction = .{ .inst = 0 }, // TODO: Put this definition somewhere... 521 .id_ref_4 = &.{initializer_id}, 522 }); 523 } 524 525 return try self.castToGeneric(decl_ptr_ty_id, result_id); 526 } 527 528 fn addFunctionDep(self: *DeclGen, decl_index: SpvModule.Decl.Index, storage_class: StorageClass) !void { 529 const target = self.getTarget(); 530 if (target.os.tag == .vulkan) { 531 // Shader entry point dependencies must be variables with Input or Output storage class 532 switch (storage_class) { 533 .Input, .Output => { 534 try self.func.decl_deps.put(self.spv.gpa, decl_index, {}); 535 }, 536 else => {}, 537 } 538 } else { 539 try self.func.decl_deps.put(self.spv.gpa, decl_index, {}); 540 } 541 } 542 543 fn castToGeneric(self: *DeclGen, type_id: IdRef, ptr_id: IdRef) !IdRef { 544 const target = self.getTarget(); 545 546 if (target.os.tag == .vulkan) { 547 return ptr_id; 548 } else { 549 const result_id = self.spv.allocId(); 550 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ 551 .id_result_type = type_id, 552 .id_result = result_id, 553 .pointer = ptr_id, 554 }); 555 return result_id; 556 } 557 } 558 559 /// Start a new SPIR-V block, Emits the label of the new block, and stores which 560 /// block we are currently generating. 561 /// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to 562 /// keep track of the previous block. 563 fn beginSpvBlock(self: *DeclGen, label: IdResult) !void { 564 try self.func.body.emit(self.spv.gpa, .OpLabel, .{ .id_result = label }); 565 self.current_block_label = label; 566 } 567 568 /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need 569 /// to emulate them in other instructions/types. This function returns, given an integer bit width (signed or unsigned, sign 570 /// included), the width of the underlying type which represents it, given the enabled features for the current target. 571 /// If the result is `null`, the largest type the target platform supports natively is not able to perform computations using 572 /// that size. In this case, multiple elements of the largest type should be used. 573 /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits. 574 /// The result is valid to be used with OpTypeInt. 575 /// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits). 576 /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers). 577 /// TODO: Should the result of this function be cached? 578 fn backingIntBits(self: *DeclGen, bits: u16) ?u16 { 579 const target = self.getTarget(); 580 581 // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function. 582 assert(bits != 0); 583 584 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively. 585 // 32-bit integers are always supported (see spec, 2.16.1, Data rules). 586 const ints = [_]struct { bits: u16, feature: ?Target.spirv.Feature }{ 587 .{ .bits = 8, .feature = .Int8 }, 588 .{ .bits = 16, .feature = .Int16 }, 589 .{ .bits = 32, .feature = null }, 590 .{ .bits = 64, .feature = .Int64 }, 591 }; 592 593 for (ints) |int| { 594 const has_feature = if (int.feature) |feature| 595 Target.spirv.featureSetHas(target.cpu.features, feature) 596 else 597 true; 598 599 if (bits <= int.bits and has_feature) { 600 return int.bits; 601 } 602 } 603 604 return null; 605 } 606 607 /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if 608 /// the Int64 capability is enabled). 609 /// Note: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits). 610 /// In theory that could also be used, but since the spec says that it only guarantees support up to 32-bit ints there 611 /// is no way of knowing whether those are actually supported. 612 /// TODO: Maybe this should be cached? 613 fn largestSupportedIntBits(self: *DeclGen) u16 { 614 const target = self.getTarget(); 615 return if (Target.spirv.featureSetHas(target.cpu.features, .Int64)) 616 64 617 else 618 32; 619 } 620 621 /// Checks whether the type is "composite int", an integer consisting of multiple native integers. These are represented by 622 /// arrays of largestSupportedIntBits(). 623 /// Asserts `ty` is an integer. 624 fn isCompositeInt(self: *DeclGen, ty: Type) bool { 625 return self.backingIntBits(ty) == null; 626 } 627 628 /// Checks whether the type can be directly translated to SPIR-V vectors 629 fn isVector(self: *DeclGen, ty: Type) bool { 630 const mod = self.module; 631 const target = self.getTarget(); 632 if (ty.zigTypeTag(mod) != .Vector) return false; 633 const elem_ty = ty.childType(mod); 634 635 const len = ty.vectorLen(mod); 636 const is_scalar = elem_ty.isNumeric(mod) or elem_ty.toIntern() == .bool_type; 637 const spirv_len = len > 1 and len <= 4; 638 const opencl_len = if (target.os.tag == .opencl) (len == 8 or len == 16) else false; 639 return is_scalar and (spirv_len or opencl_len); 640 } 641 642 fn arithmeticTypeInfo(self: *DeclGen, ty: Type) ArithmeticTypeInfo { 643 const mod = self.module; 644 const target = self.getTarget(); 645 var scalar_ty = ty.scalarType(mod); 646 if (scalar_ty.zigTypeTag(mod) == .Enum) { 647 scalar_ty = scalar_ty.intTagType(mod); 648 } 649 const vector_len = if (ty.isVector(mod)) ty.vectorLen(mod) else null; 650 return switch (scalar_ty.zigTypeTag(mod)) { 651 .Bool => ArithmeticTypeInfo{ 652 .bits = 1, // Doesn't matter for this class. 653 .backing_bits = self.backingIntBits(1).?, 654 .vector_len = vector_len, 655 .signedness = .unsigned, // Technically, but doesn't matter for this class. 656 .class = .bool, 657 }, 658 .Float => ArithmeticTypeInfo{ 659 .bits = scalar_ty.floatBits(target), 660 .backing_bits = scalar_ty.floatBits(target), // TODO: F80? 661 .vector_len = vector_len, 662 .signedness = .signed, // Technically, but doesn't matter for this class. 663 .class = .float, 664 }, 665 .Int => blk: { 666 const int_info = scalar_ty.intInfo(mod); 667 // TODO: Maybe it's useful to also return this value. 668 const maybe_backing_bits = self.backingIntBits(int_info.bits); 669 break :blk ArithmeticTypeInfo{ 670 .bits = int_info.bits, 671 .backing_bits = maybe_backing_bits orelse 0, 672 .vector_len = vector_len, 673 .signedness = int_info.signedness, 674 .class = if (maybe_backing_bits) |backing_bits| 675 if (backing_bits == int_info.bits) 676 ArithmeticTypeInfo.Class.integer 677 else 678 ArithmeticTypeInfo.Class.strange_integer 679 else 680 .composite_integer, 681 }; 682 }, 683 .Enum => unreachable, 684 .Vector => unreachable, 685 else => unreachable, // Unhandled arithmetic type 686 }; 687 } 688 689 /// Emits a bool constant in a particular representation. 690 fn constBool(self: *DeclGen, value: bool, repr: Repr) !IdRef { 691 // TODO: Cache? 692 693 const section = &self.spv.sections.types_globals_constants; 694 switch (repr) { 695 .indirect => { 696 return try self.constInt(Type.u1, @intFromBool(value), .indirect); 697 }, 698 .direct => { 699 const result_ty_id = try self.resolveType(Type.bool, .direct); 700 const result_id = self.spv.allocId(); 701 const operands = .{ 702 .id_result_type = result_ty_id, 703 .id_result = result_id, 704 }; 705 switch (value) { 706 true => try section.emit(self.spv.gpa, .OpConstantTrue, operands), 707 false => try section.emit(self.spv.gpa, .OpConstantFalse, operands), 708 } 709 return result_id; 710 }, 711 } 712 } 713 714 /// Emits an integer constant. 715 /// This function, unlike SpvModule.constInt, takes care to bitcast 716 /// the value to an unsigned int first for Kernels. 717 fn constInt(self: *DeclGen, ty: Type, value: anytype, repr: Repr) !IdRef { 718 // TODO: Cache? 719 const mod = self.module; 720 const scalar_ty = ty.scalarType(mod); 721 const int_info = scalar_ty.intInfo(mod); 722 // Use backing bits so that negatives are sign extended 723 const backing_bits = self.backingIntBits(int_info.bits).?; // Assertion failure means big int 724 725 const bits: u64 = switch (int_info.signedness) { 726 // Intcast needed to silence compile errors for when the wrong path is compiled. 727 // Lazy fix. 728 .signed => @bitCast(@as(i64, @intCast(value))), 729 .unsigned => @as(u64, @intCast(value)), 730 }; 731 732 // Manually truncate the value to the right amount of bits. 733 const truncated_bits = if (backing_bits == 64) 734 bits 735 else 736 bits & (@as(u64, 1) << @intCast(backing_bits)) - 1; 737 738 const result_ty_id = try self.resolveType(scalar_ty, repr); 739 const result_id = self.spv.allocId(); 740 741 const section = &self.spv.sections.types_globals_constants; 742 switch (backing_bits) { 743 0 => unreachable, // u0 is comptime 744 1...32 => try section.emit(self.spv.gpa, .OpConstant, .{ 745 .id_result_type = result_ty_id, 746 .id_result = result_id, 747 .value = .{ .uint32 = @truncate(truncated_bits) }, 748 }), 749 33...64 => try section.emit(self.spv.gpa, .OpConstant, .{ 750 .id_result_type = result_ty_id, 751 .id_result = result_id, 752 .value = .{ .uint64 = truncated_bits }, 753 }), 754 else => unreachable, // TODO: Large integer constants 755 } 756 757 if (!ty.isVector(mod)) { 758 return result_id; 759 } 760 761 const n = ty.vectorLen(mod); 762 const ids = try self.gpa.alloc(IdRef, n); 763 defer self.gpa.free(ids); 764 @memset(ids, result_id); 765 766 const vec_ty_id = try self.resolveType(ty, repr); 767 const vec_result_id = self.spv.allocId(); 768 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ 769 .id_result_type = vec_ty_id, 770 .id_result = vec_result_id, 771 .constituents = ids, 772 }); 773 return vec_result_id; 774 } 775 776 /// Construct a struct at runtime. 777 /// ty must be a struct type. 778 /// Constituents should be in `indirect` representation (as the elements of a struct should be). 779 /// Result is in `direct` representation. 780 fn constructStruct(self: *DeclGen, ty: Type, types: []const Type, constituents: []const IdRef) !IdRef { 781 assert(types.len == constituents.len); 782 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' 783 // operands are not constant. 784 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 785 // For now, just initialize the struct by setting the fields manually... 786 // TODO: Make this OpCompositeConstruct when we can 787 const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); 788 for (constituents, types, 0..) |constitent_id, member_ty, index| { 789 const ptr_member_ty_id = try self.ptrType(member_ty, .Function); 790 const ptr_id = try self.accessChain(ptr_member_ty_id, ptr_composite_id, &.{@as(u32, @intCast(index))}); 791 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 792 .pointer = ptr_id, 793 .object = constitent_id, 794 }); 795 } 796 return try self.load(ty, ptr_composite_id, .{}); 797 } 798 799 /// Construct a vector at runtime. 800 /// ty must be an vector type. 801 /// Constituents should be in `indirect` representation (as the elements of an vector should be). 802 /// Result is in `direct` representation. 803 fn constructVector(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { 804 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' 805 // operands are not constant. 806 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 807 // For now, just initialize the struct by setting the fields manually... 808 // TODO: Make this OpCompositeConstruct when we can 809 const mod = self.module; 810 const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); 811 const ptr_elem_ty_id = try self.ptrType(ty.elemType2(mod), .Function); 812 for (constituents, 0..) |constitent_id, index| { 813 const ptr_id = try self.accessChain(ptr_elem_ty_id, ptr_composite_id, &.{@as(u32, @intCast(index))}); 814 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 815 .pointer = ptr_id, 816 .object = constitent_id, 817 }); 818 } 819 820 return try self.load(ty, ptr_composite_id, .{}); 821 } 822 823 /// Construct an array at runtime. 824 /// ty must be an array type. 825 /// Constituents should be in `indirect` representation (as the elements of an array should be). 826 /// Result is in `direct` representation. 827 fn constructArray(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { 828 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' 829 // operands are not constant. 830 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 831 // For now, just initialize the struct by setting the fields manually... 832 // TODO: Make this OpCompositeConstruct when we can 833 const mod = self.module; 834 const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); 835 const ptr_elem_ty_id = try self.ptrType(ty.elemType2(mod), .Function); 836 for (constituents, 0..) |constitent_id, index| { 837 const ptr_id = try self.accessChain(ptr_elem_ty_id, ptr_composite_id, &.{@as(u32, @intCast(index))}); 838 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 839 .pointer = ptr_id, 840 .object = constitent_id, 841 }); 842 } 843 844 return try self.load(ty, ptr_composite_id, .{}); 845 } 846 847 /// This function generates a load for a constant in direct (ie, non-memory) representation. 848 /// When the constant is simple, it can be generated directly using OpConstant instructions. 849 /// When the constant is more complicated however, it needs to be constructed using multiple values. This 850 /// is done by emitting a sequence of instructions that initialize the value. 851 // 852 /// This function should only be called during function code generation. 853 fn constant(self: *DeclGen, ty: Type, val: Value, repr: Repr) !IdRef { 854 // Note: Using intern_map can only be used with constants that DO NOT generate any runtime code!! 855 // Ideally that should be all constants in the future, or it should be cleaned up somehow. For 856 // now, only use the intern_map on case-by-case basis by breaking to :cache. 857 if (self.intern_map.get(.{ val.toIntern(), repr })) |id| { 858 return id; 859 } 860 861 const mod = self.module; 862 const target = self.getTarget(); 863 const result_ty_id = try self.resolveType(ty, repr); 864 const ip = &mod.intern_pool; 865 866 log.debug("lowering constant: ty = {}, val = {}", .{ ty.fmt(mod), val.fmtValue(mod, null) }); 867 if (val.isUndefDeep(mod)) { 868 return self.spv.constUndef(result_ty_id); 869 } 870 871 const section = &self.spv.sections.types_globals_constants; 872 873 const cacheable_id = cache: { 874 switch (ip.indexToKey(val.toIntern())) { 875 .int_type, 876 .ptr_type, 877 .array_type, 878 .vector_type, 879 .opt_type, 880 .anyframe_type, 881 .error_union_type, 882 .simple_type, 883 .struct_type, 884 .anon_struct_type, 885 .union_type, 886 .opaque_type, 887 .enum_type, 888 .func_type, 889 .error_set_type, 890 .inferred_error_set_type, 891 => unreachable, // types, not values 892 893 .undef => unreachable, // handled above 894 895 .variable, 896 .extern_func, 897 .func, 898 .enum_literal, 899 .empty_enum_value, 900 => unreachable, // non-runtime values 901 902 .simple_value => |simple_value| switch (simple_value) { 903 .undefined, 904 .void, 905 .null, 906 .empty_struct, 907 .@"unreachable", 908 .generic_poison, 909 => unreachable, // non-runtime values 910 911 .false, .true => break :cache try self.constBool(val.toBool(), repr), 912 }, 913 .int => { 914 if (ty.isSignedInt(mod)) { 915 break :cache try self.constInt(ty, val.toSignedInt(mod), repr); 916 } else { 917 break :cache try self.constInt(ty, val.toUnsignedInt(mod), repr); 918 } 919 }, 920 .float => { 921 const lit: spec.LiteralContextDependentNumber = switch (ty.floatBits(target)) { 922 16 => .{ .uint32 = @as(u16, @bitCast(val.toFloat(f16, mod))) }, 923 32 => .{ .float32 = val.toFloat(f32, mod) }, 924 64 => .{ .float64 = val.toFloat(f64, mod) }, 925 80, 128 => unreachable, // TODO 926 else => unreachable, 927 }; 928 const result_id = self.spv.allocId(); 929 try section.emit(self.spv.gpa, .OpConstant, .{ 930 .id_result_type = result_ty_id, 931 .id_result = result_id, 932 .value = lit, 933 }); 934 break :cache result_id; 935 }, 936 .err => |err| { 937 const value = try mod.getErrorValue(err.name); 938 break :cache try self.constInt(ty, value, repr); 939 }, 940 .error_union => |error_union| { 941 // TODO: Error unions may be constructed with constant instructions if the payload type 942 // allows it. For now, just generate it here regardless. 943 const err_int_ty = try mod.errorIntType(); 944 const err_ty = switch (error_union.val) { 945 .err_name => ty.errorUnionSet(mod), 946 .payload => err_int_ty, 947 }; 948 const err_val = switch (error_union.val) { 949 .err_name => |err_name| Value.fromInterned((try mod.intern(.{ .err = .{ 950 .ty = ty.errorUnionSet(mod).toIntern(), 951 .name = err_name, 952 } }))), 953 .payload => try mod.intValue(err_int_ty, 0), 954 }; 955 const payload_ty = ty.errorUnionPayload(mod); 956 const eu_layout = self.errorUnionLayout(payload_ty); 957 if (!eu_layout.payload_has_bits) { 958 // We use the error type directly as the type. 959 break :cache try self.constant(err_ty, err_val, .indirect); 960 } 961 962 const payload_val = Value.fromInterned(switch (error_union.val) { 963 .err_name => try mod.intern(.{ .undef = payload_ty.toIntern() }), 964 .payload => |payload| payload, 965 }); 966 967 var constituents: [2]IdRef = undefined; 968 var types: [2]Type = undefined; 969 if (eu_layout.error_first) { 970 constituents[0] = try self.constant(err_ty, err_val, .indirect); 971 constituents[1] = try self.constant(payload_ty, payload_val, .indirect); 972 types = .{ err_ty, payload_ty }; 973 } else { 974 constituents[0] = try self.constant(payload_ty, payload_val, .indirect); 975 constituents[1] = try self.constant(err_ty, err_val, .indirect); 976 types = .{ payload_ty, err_ty }; 977 } 978 979 return try self.constructStruct(ty, &types, &constituents); 980 }, 981 .enum_tag => { 982 const int_val = try val.intFromEnum(ty, mod); 983 const int_ty = ty.intTagType(mod); 984 break :cache try self.constant(int_ty, int_val, repr); 985 }, 986 .ptr => return self.constantPtr(val), 987 .slice => |slice| { 988 const ptr_ty = ty.slicePtrFieldType(mod); 989 const ptr_id = try self.constantPtr(Value.fromInterned(slice.ptr)); 990 const len_id = try self.constant(Type.usize, Value.fromInterned(slice.len), .indirect); 991 return self.constructStruct( 992 ty, 993 &.{ ptr_ty, Type.usize }, 994 &.{ ptr_id, len_id }, 995 ); 996 }, 997 .opt => { 998 const payload_ty = ty.optionalChild(mod); 999 const maybe_payload_val = val.optionalValue(mod); 1000 1001 if (!payload_ty.hasRuntimeBits(mod)) { 1002 break :cache try self.constBool(maybe_payload_val != null, .indirect); 1003 } else if (ty.optionalReprIsPayload(mod)) { 1004 // Optional representation is a nullable pointer or slice. 1005 if (maybe_payload_val) |payload_val| { 1006 return try self.constant(payload_ty, payload_val, .indirect); 1007 } else { 1008 break :cache try self.spv.constNull(result_ty_id); 1009 } 1010 } 1011 1012 // Optional representation is a structure. 1013 // { Payload, Bool } 1014 1015 const has_pl_id = try self.constBool(maybe_payload_val != null, .indirect); 1016 const payload_id = if (maybe_payload_val) |payload_val| 1017 try self.constant(payload_ty, payload_val, .indirect) 1018 else 1019 try self.spv.constUndef(try self.resolveType(payload_ty, .indirect)); 1020 1021 return try self.constructStruct( 1022 ty, 1023 &.{ payload_ty, Type.bool }, 1024 &.{ payload_id, has_pl_id }, 1025 ); 1026 }, 1027 .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) { 1028 inline .array_type, .vector_type => |array_type, tag| { 1029 const elem_ty = Type.fromInterned(array_type.child); 1030 1031 const constituents = try self.gpa.alloc(IdRef, @intCast(ty.arrayLenIncludingSentinel(mod))); 1032 defer self.gpa.free(constituents); 1033 1034 switch (aggregate.storage) { 1035 .bytes => |bytes| { 1036 // TODO: This is really space inefficient, perhaps there is a better 1037 // way to do it? 1038 for (constituents, bytes.toSlice(constituents.len, ip)) |*constituent, byte| { 1039 constituent.* = try self.constInt(elem_ty, byte, .indirect); 1040 } 1041 }, 1042 .elems => |elems| { 1043 for (constituents, elems) |*constituent, elem| { 1044 constituent.* = try self.constant(elem_ty, Value.fromInterned(elem), .indirect); 1045 } 1046 }, 1047 .repeated_elem => |elem| { 1048 @memset(constituents, try self.constant(elem_ty, Value.fromInterned(elem), .indirect)); 1049 }, 1050 } 1051 1052 switch (tag) { 1053 .array_type => return self.constructArray(ty, constituents), 1054 .vector_type => return self.constructVector(ty, constituents), 1055 else => unreachable, 1056 } 1057 }, 1058 .struct_type => { 1059 const struct_type = mod.typeToStruct(ty).?; 1060 if (struct_type.layout == .@"packed") { 1061 return self.todo("packed struct constants", .{}); 1062 } 1063 1064 var types = std.ArrayList(Type).init(self.gpa); 1065 defer types.deinit(); 1066 1067 var constituents = std.ArrayList(IdRef).init(self.gpa); 1068 defer constituents.deinit(); 1069 1070 var it = struct_type.iterateRuntimeOrder(ip); 1071 while (it.next()) |field_index| { 1072 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); 1073 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { 1074 // This is a zero-bit field - we only needed it for the alignment. 1075 continue; 1076 } 1077 1078 // TODO: Padding? 1079 const field_val = try val.fieldValue(mod, field_index); 1080 const field_id = try self.constant(field_ty, field_val, .indirect); 1081 1082 try types.append(field_ty); 1083 try constituents.append(field_id); 1084 } 1085 1086 return try self.constructStruct(ty, types.items, constituents.items); 1087 }, 1088 .anon_struct_type => unreachable, // TODO 1089 else => unreachable, 1090 }, 1091 .un => |un| { 1092 const active_field = ty.unionTagFieldIndex(Value.fromInterned(un.tag), mod).?; 1093 const union_obj = mod.typeToUnion(ty).?; 1094 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[active_field]); 1095 const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(mod)) 1096 try self.constant(field_ty, Value.fromInterned(un.val), .direct) 1097 else 1098 null; 1099 return try self.unionInit(ty, active_field, payload); 1100 }, 1101 .memoized_call => unreachable, 1102 } 1103 }; 1104 1105 try self.intern_map.putNoClobber(self.gpa, .{ val.toIntern(), repr }, cacheable_id); 1106 1107 return cacheable_id; 1108 } 1109 1110 fn constantPtr(self: *DeclGen, ptr_val: Value) Error!IdRef { 1111 // TODO: Caching?? 1112 1113 const zcu = self.module; 1114 1115 if (ptr_val.isUndef(zcu)) { 1116 const result_ty = ptr_val.typeOf(zcu); 1117 const result_ty_id = try self.resolveType(result_ty, .direct); 1118 return self.spv.constUndef(result_ty_id); 1119 } 1120 1121 var arena = std.heap.ArenaAllocator.init(self.gpa); 1122 defer arena.deinit(); 1123 1124 const derivation = try ptr_val.pointerDerivation(arena.allocator(), zcu); 1125 return self.derivePtr(derivation); 1126 } 1127 1128 fn derivePtr(self: *DeclGen, derivation: Value.PointerDeriveStep) Error!IdRef { 1129 const zcu = self.module; 1130 switch (derivation) { 1131 .comptime_alloc_ptr, .comptime_field_ptr => unreachable, 1132 .int => |int| { 1133 const result_ty_id = try self.resolveType(int.ptr_ty, .direct); 1134 // TODO: This can probably be an OpSpecConstantOp Bitcast, but 1135 // that is not implemented by Mesa yet. Therefore, just generate it 1136 // as a runtime operation. 1137 const result_ptr_id = self.spv.allocId(); 1138 try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{ 1139 .id_result_type = result_ty_id, 1140 .id_result = result_ptr_id, 1141 .integer_value = try self.constant(Type.usize, try zcu.intValue(Type.usize, int.addr), .direct), 1142 }); 1143 return result_ptr_id; 1144 }, 1145 .decl_ptr => |decl| { 1146 const result_ptr_ty = try zcu.declPtr(decl).declPtrType(zcu); 1147 return self.constantDeclRef(result_ptr_ty, decl); 1148 }, 1149 .anon_decl_ptr => |ad| { 1150 const result_ptr_ty = Type.fromInterned(ad.orig_ty); 1151 return self.constantAnonDeclRef(result_ptr_ty, ad); 1152 }, 1153 .eu_payload_ptr => @panic("TODO"), 1154 .opt_payload_ptr => @panic("TODO"), 1155 .field_ptr => |field| { 1156 const parent_ptr_id = try self.derivePtr(field.parent.*); 1157 const parent_ptr_ty = try field.parent.ptrType(zcu); 1158 return self.structFieldPtr(field.result_ptr_ty, parent_ptr_ty, parent_ptr_id, field.field_idx); 1159 }, 1160 .elem_ptr => |elem| { 1161 const parent_ptr_id = try self.derivePtr(elem.parent.*); 1162 const parent_ptr_ty = try elem.parent.ptrType(zcu); 1163 const index_id = try self.constInt(Type.usize, elem.elem_idx, .direct); 1164 return self.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id); 1165 }, 1166 .offset_and_cast => |oac| { 1167 const parent_ptr_id = try self.derivePtr(oac.parent.*); 1168 const parent_ptr_ty = try oac.parent.ptrType(zcu); 1169 disallow: { 1170 if (oac.byte_offset != 0) break :disallow; 1171 // Allow changing the pointer type child only to restructure arrays. 1172 // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T. 1173 const src_base_ty = parent_ptr_ty.arrayBase(zcu)[0]; 1174 const dest_base_ty = oac.new_ptr_ty.arrayBase(zcu)[0]; 1175 if (self.getTarget().os.tag == .vulkan and src_base_ty.toIntern() != dest_base_ty.toIntern()) break :disallow; 1176 1177 const result_ty_id = try self.resolveType(oac.new_ptr_ty, .direct); 1178 const result_ptr_id = self.spv.allocId(); 1179 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 1180 .id_result_type = result_ty_id, 1181 .id_result = result_ptr_id, 1182 .operand = parent_ptr_id, 1183 }); 1184 return result_ptr_id; 1185 } 1186 return self.fail("Cannot perform pointer cast: '{}' to '{}'", .{ 1187 parent_ptr_ty.fmt(zcu), 1188 oac.new_ptr_ty.fmt(zcu), 1189 }); 1190 }, 1191 } 1192 } 1193 1194 fn constantAnonDeclRef( 1195 self: *DeclGen, 1196 ty: Type, 1197 anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl, 1198 ) !IdRef { 1199 // TODO: Merge this function with constantDeclRef. 1200 1201 const mod = self.module; 1202 const ip = &mod.intern_pool; 1203 const ty_id = try self.resolveType(ty, .direct); 1204 const decl_val = anon_decl.val; 1205 const decl_ty = Type.fromInterned(ip.typeOf(decl_val)); 1206 1207 if (Value.fromInterned(decl_val).getFunction(mod)) |func| { 1208 _ = func; 1209 unreachable; // TODO 1210 } else if (Value.fromInterned(decl_val).getExternFunc(mod)) |func| { 1211 _ = func; 1212 unreachable; 1213 } 1214 1215 // const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; 1216 if (!decl_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { 1217 // Pointer to nothing - return undefoined 1218 return self.spv.constUndef(ty_id); 1219 } 1220 1221 if (decl_ty.zigTypeTag(mod) == .Fn) { 1222 unreachable; // TODO 1223 } 1224 1225 // Anon decl refs are always generic. 1226 assert(ty.ptrAddressSpace(mod) == .generic); 1227 const decl_ptr_ty_id = try self.ptrType(decl_ty, .Generic); 1228 const ptr_id = try self.resolveAnonDecl(decl_val); 1229 1230 if (decl_ptr_ty_id != ty_id) { 1231 // Differing pointer types, insert a cast. 1232 const casted_ptr_id = self.spv.allocId(); 1233 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 1234 .id_result_type = ty_id, 1235 .id_result = casted_ptr_id, 1236 .operand = ptr_id, 1237 }); 1238 return casted_ptr_id; 1239 } else { 1240 return ptr_id; 1241 } 1242 } 1243 1244 fn constantDeclRef(self: *DeclGen, ty: Type, decl_index: InternPool.DeclIndex) !IdRef { 1245 const mod = self.module; 1246 const ty_id = try self.resolveType(ty, .direct); 1247 const decl = mod.declPtr(decl_index); 1248 1249 switch (mod.intern_pool.indexToKey(decl.val.ip_index)) { 1250 .func => { 1251 // TODO: Properly lower function pointers. For now we are going to hack around it and 1252 // just generate an empty pointer. Function pointers are represented by a pointer to usize. 1253 return try self.spv.constUndef(ty_id); 1254 }, 1255 .extern_func => unreachable, // TODO 1256 else => {}, 1257 } 1258 1259 if (!decl.typeOf(mod).isFnOrHasRuntimeBitsIgnoreComptime(mod)) { 1260 // Pointer to nothing - return undefined. 1261 return self.spv.constUndef(ty_id); 1262 } 1263 1264 const spv_decl_index = try self.object.resolveDecl(mod, decl_index); 1265 const spv_decl = self.spv.declPtr(spv_decl_index); 1266 1267 const decl_id = switch (spv_decl.kind) { 1268 .func => unreachable, // TODO: Is this possible? 1269 .global, .invocation_global => spv_decl.result_id, 1270 }; 1271 1272 const final_storage_class = self.spvStorageClass(decl.@"addrspace"); 1273 try self.addFunctionDep(spv_decl_index, final_storage_class); 1274 1275 const decl_ptr_ty_id = try self.ptrType(decl.typeOf(mod), final_storage_class); 1276 1277 const ptr_id = switch (final_storage_class) { 1278 .Generic => try self.castToGeneric(decl_ptr_ty_id, decl_id), 1279 else => decl_id, 1280 }; 1281 1282 if (decl_ptr_ty_id != ty_id) { 1283 // Differing pointer types, insert a cast. 1284 const casted_ptr_id = self.spv.allocId(); 1285 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 1286 .id_result_type = ty_id, 1287 .id_result = casted_ptr_id, 1288 .operand = ptr_id, 1289 }); 1290 return casted_ptr_id; 1291 } else { 1292 return ptr_id; 1293 } 1294 } 1295 1296 // Turn a Zig type's name into a cache reference. 1297 fn resolveTypeName(self: *DeclGen, ty: Type) ![]const u8 { 1298 var name = std.ArrayList(u8).init(self.gpa); 1299 defer name.deinit(); 1300 try ty.print(name.writer(), self.module); 1301 return try name.toOwnedSlice(); 1302 } 1303 1304 /// Create an integer type suitable for storing at least 'bits' bits. 1305 /// The integer type that is returned by this function is the type that is used to perform 1306 /// actual operations (as well as store) a Zig type of a particular number of bits. To create 1307 /// a type with an exact size, use SpvModule.intType. 1308 fn intType(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !IdRef { 1309 const backing_bits = self.backingIntBits(bits) orelse { 1310 // TODO: Integers too big for any native type are represented as "composite integers": 1311 // An array of largestSupportedIntBits. 1312 return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits }); 1313 }; 1314 1315 // Kernel only supports unsigned ints. 1316 if (self.getTarget().os.tag == .vulkan) { 1317 return self.spv.intType(signedness, backing_bits); 1318 } 1319 1320 return self.spv.intType(.unsigned, backing_bits); 1321 } 1322 1323 fn arrayType(self: *DeclGen, len: u32, child_ty: IdRef) !IdRef { 1324 // TODO: Cache?? 1325 const len_id = try self.constInt(Type.u32, len, .direct); 1326 const result_id = self.spv.allocId(); 1327 1328 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypeArray, .{ 1329 .id_result = result_id, 1330 .element_type = child_ty, 1331 .length = len_id, 1332 }); 1333 return result_id; 1334 } 1335 1336 fn ptrType(self: *DeclGen, child_ty: Type, storage_class: StorageClass) !IdRef { 1337 const key = .{ child_ty.toIntern(), storage_class }; 1338 const entry = try self.ptr_types.getOrPut(self.gpa, key); 1339 if (entry.found_existing) { 1340 const fwd_id = entry.value_ptr.ty_id; 1341 if (!entry.value_ptr.fwd_emitted) { 1342 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypeForwardPointer, .{ 1343 .pointer_type = fwd_id, 1344 .storage_class = storage_class, 1345 }); 1346 entry.value_ptr.fwd_emitted = true; 1347 } 1348 return fwd_id; 1349 } 1350 1351 const result_id = self.spv.allocId(); 1352 entry.value_ptr.* = .{ 1353 .ty_id = result_id, 1354 .fwd_emitted = false, 1355 }; 1356 1357 const child_ty_id = try self.resolveType(child_ty, .indirect); 1358 1359 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ 1360 .id_result = result_id, 1361 .storage_class = storage_class, 1362 .type = child_ty_id, 1363 }); 1364 1365 return result_id; 1366 } 1367 1368 fn functionType(self: *DeclGen, return_ty: Type, param_types: []const Type) !IdRef { 1369 // TODO: Cache?? 1370 1371 const param_ids = try self.gpa.alloc(IdRef, param_types.len); 1372 defer self.gpa.free(param_ids); 1373 1374 for (param_types, param_ids) |param_ty, *param_id| { 1375 param_id.* = try self.resolveType(param_ty, .direct); 1376 } 1377 1378 const ty_id = self.spv.allocId(); 1379 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypeFunction, .{ 1380 .id_result = ty_id, 1381 .return_type = try self.resolveFnReturnType(return_ty), 1382 .id_ref_2 = param_ids, 1383 }); 1384 1385 return ty_id; 1386 } 1387 1388 /// Generate a union type. Union types are always generated with the 1389 /// most aligned field active. If the tag alignment is greater 1390 /// than that of the payload, a regular union (non-packed, with both tag and 1391 /// payload), will be generated as follows: 1392 /// struct { 1393 /// tag: TagType, 1394 /// payload: MostAlignedFieldType, 1395 /// payload_padding: [payload_size - @sizeOf(MostAlignedFieldType)]u8, 1396 /// padding: [padding_size]u8, 1397 /// } 1398 /// If the payload alignment is greater than that of the tag: 1399 /// struct { 1400 /// payload: MostAlignedFieldType, 1401 /// payload_padding: [payload_size - @sizeOf(MostAlignedFieldType)]u8, 1402 /// tag: TagType, 1403 /// padding: [padding_size]u8, 1404 /// } 1405 /// If any of the fields' size is 0, it will be omitted. 1406 fn resolveUnionType(self: *DeclGen, ty: Type) !IdRef { 1407 const mod = self.module; 1408 const ip = &mod.intern_pool; 1409 const union_obj = mod.typeToUnion(ty).?; 1410 1411 if (union_obj.getLayout(ip) == .@"packed") { 1412 return self.todo("packed union types", .{}); 1413 } 1414 1415 const layout = self.unionLayout(ty); 1416 if (!layout.has_payload) { 1417 // No payload, so represent this as just the tag type. 1418 return try self.resolveType(Type.fromInterned(union_obj.enum_tag_ty), .indirect); 1419 } 1420 1421 var member_types: [4]IdRef = undefined; 1422 var member_names: [4][]const u8 = undefined; 1423 1424 const u8_ty_id = try self.resolveType(Type.u8, .direct); // TODO: What if Int8Type is not enabled? 1425 1426 if (layout.tag_size != 0) { 1427 const tag_ty_id = try self.resolveType(Type.fromInterned(union_obj.enum_tag_ty), .indirect); 1428 member_types[layout.tag_index] = tag_ty_id; 1429 member_names[layout.tag_index] = "(tag)"; 1430 } 1431 1432 if (layout.payload_size != 0) { 1433 const payload_ty_id = try self.resolveType(layout.payload_ty, .indirect); 1434 member_types[layout.payload_index] = payload_ty_id; 1435 member_names[layout.payload_index] = "(payload)"; 1436 } 1437 1438 if (layout.payload_padding_size != 0) { 1439 const payload_padding_ty_id = try self.arrayType(@intCast(layout.payload_padding_size), u8_ty_id); 1440 member_types[layout.payload_padding_index] = payload_padding_ty_id; 1441 member_names[layout.payload_padding_index] = "(payload padding)"; 1442 } 1443 1444 if (layout.padding_size != 0) { 1445 const padding_ty_id = try self.arrayType(@intCast(layout.padding_size), u8_ty_id); 1446 member_types[layout.padding_index] = padding_ty_id; 1447 member_names[layout.padding_index] = "(padding)"; 1448 } 1449 1450 const result_id = try self.spv.structType(member_types[0..layout.total_fields], member_names[0..layout.total_fields]); 1451 const type_name = try self.resolveTypeName(ty); 1452 defer self.gpa.free(type_name); 1453 try self.spv.debugName(result_id, type_name); 1454 return result_id; 1455 } 1456 1457 fn resolveFnReturnType(self: *DeclGen, ret_ty: Type) !IdRef { 1458 const mod = self.module; 1459 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) { 1460 // If the return type is an error set or an error union, then we make this 1461 // anyerror return type instead, so that it can be coerced into a function 1462 // pointer type which has anyerror as the return type. 1463 if (ret_ty.isError(mod)) { 1464 return self.resolveType(Type.anyerror, .direct); 1465 } else { 1466 return self.resolveType(Type.void, .direct); 1467 } 1468 } 1469 1470 return try self.resolveType(ret_ty, .direct); 1471 } 1472 1473 /// Turn a Zig type into a SPIR-V Type, and return a reference to it. 1474 fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!IdRef { 1475 if (self.intern_map.get(.{ ty.toIntern(), repr })) |id| { 1476 return id; 1477 } 1478 1479 const id = try self.resolveTypeInner(ty, repr); 1480 try self.intern_map.put(self.gpa, .{ ty.toIntern(), repr }, id); 1481 return id; 1482 } 1483 1484 fn resolveTypeInner(self: *DeclGen, ty: Type, repr: Repr) Error!IdRef { 1485 const mod = self.module; 1486 const ip = &mod.intern_pool; 1487 log.debug("resolveType: ty = {}", .{ty.fmt(mod)}); 1488 const target = self.getTarget(); 1489 1490 const section = &self.spv.sections.types_globals_constants; 1491 1492 switch (ty.zigTypeTag(mod)) { 1493 .NoReturn => { 1494 assert(repr == .direct); 1495 return try self.spv.voidType(); 1496 }, 1497 .Void => switch (repr) { 1498 .direct => { 1499 return try self.spv.voidType(); 1500 }, 1501 // Pointers to void 1502 .indirect => { 1503 const result_id = self.spv.allocId(); 1504 try section.emit(self.spv.gpa, .OpTypeOpaque, .{ 1505 .id_result = result_id, 1506 .literal_string = "void", 1507 }); 1508 return result_id; 1509 }, 1510 }, 1511 .Bool => switch (repr) { 1512 .direct => return try self.spv.boolType(), 1513 .indirect => return try self.resolveType(Type.u1, .indirect), 1514 }, 1515 .Int => { 1516 const int_info = ty.intInfo(mod); 1517 if (int_info.bits == 0) { 1518 // Some times, the backend will be asked to generate a pointer to i0. OpTypeInt 1519 // with 0 bits is invalid, so return an opaque type in this case. 1520 assert(repr == .indirect); 1521 const result_id = self.spv.allocId(); 1522 try section.emit(self.spv.gpa, .OpTypeOpaque, .{ 1523 .id_result = result_id, 1524 .literal_string = "u0", 1525 }); 1526 return result_id; 1527 } 1528 return try self.intType(int_info.signedness, int_info.bits); 1529 }, 1530 .Enum => { 1531 const tag_ty = ty.intTagType(mod); 1532 return try self.resolveType(tag_ty, repr); 1533 }, 1534 .Float => { 1535 // We can (and want) not really emulate floating points with other floating point types like with the integer types, 1536 // so if the float is not supported, just return an error. 1537 const bits = ty.floatBits(target); 1538 const supported = switch (bits) { 1539 16 => Target.spirv.featureSetHas(target.cpu.features, .Float16), 1540 // 32-bit floats are always supported (see spec, 2.16.1, Data rules). 1541 32 => true, 1542 64 => Target.spirv.featureSetHas(target.cpu.features, .Float64), 1543 else => false, 1544 }; 1545 1546 if (!supported) { 1547 return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits}); 1548 } 1549 1550 return try self.spv.floatType(bits); 1551 }, 1552 .Array => { 1553 const elem_ty = ty.childType(mod); 1554 const elem_ty_id = try self.resolveType(elem_ty, .indirect); 1555 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(mod)) orelse { 1556 return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(mod)}); 1557 }; 1558 1559 if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) { 1560 // The size of the array would be 0, but that is not allowed in SPIR-V. 1561 // This path can be reached when the backend is asked to generate a pointer to 1562 // an array of some zero-bit type. This should always be an indirect path. 1563 assert(repr == .indirect); 1564 1565 // We cannot use the child type here, so just use an opaque type. 1566 const result_id = self.spv.allocId(); 1567 try section.emit(self.spv.gpa, .OpTypeOpaque, .{ 1568 .id_result = result_id, 1569 .literal_string = "zero-sized array", 1570 }); 1571 return result_id; 1572 } else if (total_len == 0) { 1573 // The size of the array would be 0, but that is not allowed in SPIR-V. 1574 // This path can be reached for example when there is a slicing of a pointer 1575 // that produces a zero-length array. In all cases where this type can be generated, 1576 // this should be an indirect path. 1577 assert(repr == .indirect); 1578 1579 // In this case, we have an array of a non-zero sized type. In this case, 1580 // generate an array of 1 element instead, so that ptr_elem_ptr instructions 1581 // can be lowered to ptrAccessChain instead of manually performing the math. 1582 return try self.arrayType(1, elem_ty_id); 1583 } else { 1584 return try self.arrayType(total_len, elem_ty_id); 1585 } 1586 }, 1587 .Fn => switch (repr) { 1588 .direct => { 1589 const fn_info = mod.typeToFunc(ty).?; 1590 1591 comptime assert(zig_call_abi_ver == 3); 1592 switch (fn_info.cc) { 1593 .Unspecified, .Kernel, .Fragment, .Vertex, .C => {}, 1594 else => unreachable, // TODO 1595 } 1596 1597 // TODO: Put this somewhere in Sema.zig 1598 if (fn_info.is_var_args) 1599 return self.fail("VarArgs functions are unsupported for SPIR-V", .{}); 1600 1601 // Note: Logic is different from functionType(). 1602 const param_ty_ids = try self.gpa.alloc(IdRef, fn_info.param_types.len); 1603 defer self.gpa.free(param_ty_ids); 1604 var param_index: usize = 0; 1605 for (fn_info.param_types.get(ip)) |param_ty_index| { 1606 const param_ty = Type.fromInterned(param_ty_index); 1607 if (!param_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; 1608 1609 param_ty_ids[param_index] = try self.resolveType(param_ty, .direct); 1610 param_index += 1; 1611 } 1612 1613 const return_ty_id = try self.resolveFnReturnType(Type.fromInterned(fn_info.return_type)); 1614 1615 const result_id = self.spv.allocId(); 1616 try section.emit(self.spv.gpa, .OpTypeFunction, .{ 1617 .id_result = result_id, 1618 .return_type = return_ty_id, 1619 .id_ref_2 = param_ty_ids[0..param_index], 1620 }); 1621 1622 return result_id; 1623 }, 1624 .indirect => { 1625 // TODO: Represent function pointers properly. 1626 // For now, just use an usize type. 1627 return try self.resolveType(Type.usize, .indirect); 1628 }, 1629 }, 1630 .Pointer => { 1631 const ptr_info = ty.ptrInfo(mod); 1632 1633 const storage_class = self.spvStorageClass(ptr_info.flags.address_space); 1634 const ptr_ty_id = try self.ptrType(Type.fromInterned(ptr_info.child), storage_class); 1635 1636 if (ptr_info.flags.size != .Slice) { 1637 return ptr_ty_id; 1638 } 1639 1640 const size_ty_id = try self.resolveType(Type.usize, .direct); 1641 return self.spv.structType( 1642 &.{ ptr_ty_id, size_ty_id }, 1643 &.{ "ptr", "len" }, 1644 ); 1645 }, 1646 .Vector => { 1647 const elem_ty = ty.childType(mod); 1648 // TODO: Make `.direct`. 1649 const elem_ty_id = try self.resolveType(elem_ty, .indirect); 1650 const len = ty.vectorLen(mod); 1651 1652 if (self.isVector(ty)) { 1653 return try self.spv.vectorType(len, elem_ty_id); 1654 } else { 1655 return try self.arrayType(len, elem_ty_id); 1656 } 1657 }, 1658 .Struct => { 1659 const struct_type = switch (ip.indexToKey(ty.toIntern())) { 1660 .anon_struct_type => |tuple| { 1661 const member_types = try self.gpa.alloc(IdRef, tuple.values.len); 1662 defer self.gpa.free(member_types); 1663 1664 var member_index: usize = 0; 1665 for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| { 1666 if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; 1667 1668 member_types[member_index] = try self.resolveType(Type.fromInterned(field_ty), .indirect); 1669 member_index += 1; 1670 } 1671 1672 const result_id = try self.spv.structType(member_types[0..member_index], null); 1673 const type_name = try self.resolveTypeName(ty); 1674 defer self.gpa.free(type_name); 1675 try self.spv.debugName(result_id, type_name); 1676 return result_id; 1677 }, 1678 .struct_type => ip.loadStructType(ty.toIntern()), 1679 else => unreachable, 1680 }; 1681 1682 if (struct_type.layout == .@"packed") { 1683 return try self.resolveType(Type.fromInterned(struct_type.backingIntType(ip).*), .direct); 1684 } 1685 1686 var member_types = std.ArrayList(IdRef).init(self.gpa); 1687 defer member_types.deinit(); 1688 1689 var member_names = std.ArrayList([]const u8).init(self.gpa); 1690 defer member_names.deinit(); 1691 1692 var it = struct_type.iterateRuntimeOrder(ip); 1693 while (it.next()) |field_index| { 1694 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); 1695 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { 1696 // This is a zero-bit field - we only needed it for the alignment. 1697 continue; 1698 } 1699 1700 const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse 1701 try ip.getOrPutStringFmt(mod.gpa, "{d}", .{field_index}, .no_embedded_nulls); 1702 try member_types.append(try self.resolveType(field_ty, .indirect)); 1703 try member_names.append(field_name.toSlice(ip)); 1704 } 1705 1706 const result_id = try self.spv.structType(member_types.items, member_names.items); 1707 const type_name = try self.resolveTypeName(ty); 1708 defer self.gpa.free(type_name); 1709 try self.spv.debugName(result_id, type_name); 1710 return result_id; 1711 }, 1712 .Optional => { 1713 const payload_ty = ty.optionalChild(mod); 1714 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { 1715 // Just use a bool. 1716 // Note: Always generate the bool with indirect format, to save on some sanity 1717 // Perform the conversion to a direct bool when the field is extracted. 1718 return try self.resolveType(Type.bool, .indirect); 1719 } 1720 1721 const payload_ty_id = try self.resolveType(payload_ty, .indirect); 1722 if (ty.optionalReprIsPayload(mod)) { 1723 // Optional is actually a pointer or a slice. 1724 return payload_ty_id; 1725 } 1726 1727 const bool_ty_id = try self.resolveType(Type.bool, .indirect); 1728 1729 return try self.spv.structType( 1730 &.{ payload_ty_id, bool_ty_id }, 1731 &.{ "payload", "valid" }, 1732 ); 1733 }, 1734 .Union => return try self.resolveUnionType(ty), 1735 .ErrorSet => return try self.resolveType(Type.u16, repr), 1736 .ErrorUnion => { 1737 const payload_ty = ty.errorUnionPayload(mod); 1738 const error_ty_id = try self.resolveType(Type.anyerror, .indirect); 1739 1740 const eu_layout = self.errorUnionLayout(payload_ty); 1741 if (!eu_layout.payload_has_bits) { 1742 return error_ty_id; 1743 } 1744 1745 const payload_ty_id = try self.resolveType(payload_ty, .indirect); 1746 1747 var member_types: [2]IdRef = undefined; 1748 var member_names: [2][]const u8 = undefined; 1749 if (eu_layout.error_first) { 1750 // Put the error first 1751 member_types = .{ error_ty_id, payload_ty_id }; 1752 member_names = .{ "error", "payload" }; 1753 // TODO: ABI padding? 1754 } else { 1755 // Put the payload first. 1756 member_types = .{ payload_ty_id, error_ty_id }; 1757 member_names = .{ "payload", "error" }; 1758 // TODO: ABI padding? 1759 } 1760 1761 return try self.spv.structType(&member_types, &member_names); 1762 }, 1763 .Opaque => { 1764 const type_name = try self.resolveTypeName(ty); 1765 defer self.gpa.free(type_name); 1766 1767 const result_id = self.spv.allocId(); 1768 try section.emit(self.spv.gpa, .OpTypeOpaque, .{ 1769 .id_result = result_id, 1770 .literal_string = type_name, 1771 }); 1772 return result_id; 1773 }, 1774 1775 .Null, 1776 .Undefined, 1777 .EnumLiteral, 1778 .ComptimeFloat, 1779 .ComptimeInt, 1780 .Type, 1781 => unreachable, // Must be comptime. 1782 1783 .Frame, .AnyFrame => unreachable, // TODO 1784 } 1785 } 1786 1787 fn spvStorageClass(self: *DeclGen, as: std.builtin.AddressSpace) StorageClass { 1788 const target = self.getTarget(); 1789 return switch (as) { 1790 .generic => switch (target.os.tag) { 1791 .vulkan => .Private, 1792 else => .Generic, 1793 }, 1794 .shared => .Workgroup, 1795 .local => .Private, 1796 .global => .CrossWorkgroup, 1797 .constant => .UniformConstant, 1798 .input => .Input, 1799 .output => .Output, 1800 .uniform => .Uniform, 1801 .gs, 1802 .fs, 1803 .ss, 1804 .param, 1805 .flash, 1806 .flash1, 1807 .flash2, 1808 .flash3, 1809 .flash4, 1810 .flash5, 1811 => unreachable, 1812 }; 1813 } 1814 1815 const ErrorUnionLayout = struct { 1816 payload_has_bits: bool, 1817 error_first: bool, 1818 1819 fn errorFieldIndex(self: @This()) u32 { 1820 assert(self.payload_has_bits); 1821 return if (self.error_first) 0 else 1; 1822 } 1823 1824 fn payloadFieldIndex(self: @This()) u32 { 1825 assert(self.payload_has_bits); 1826 return if (self.error_first) 1 else 0; 1827 } 1828 }; 1829 1830 fn errorUnionLayout(self: *DeclGen, payload_ty: Type) ErrorUnionLayout { 1831 const mod = self.module; 1832 1833 const error_align = Type.anyerror.abiAlignment(mod); 1834 const payload_align = payload_ty.abiAlignment(mod); 1835 1836 const error_first = error_align.compare(.gt, payload_align); 1837 return .{ 1838 .payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(mod), 1839 .error_first = error_first, 1840 }; 1841 } 1842 1843 const UnionLayout = struct { 1844 /// If false, this union is represented 1845 /// by only an integer of the tag type. 1846 has_payload: bool, 1847 tag_size: u32, 1848 tag_index: u32, 1849 /// Note: This is the size of the payload type itself, NOT the size of the ENTIRE payload. 1850 /// Use `has_payload` instead!! 1851 payload_ty: Type, 1852 payload_size: u32, 1853 payload_index: u32, 1854 payload_padding_size: u32, 1855 payload_padding_index: u32, 1856 padding_size: u32, 1857 padding_index: u32, 1858 total_fields: u32, 1859 }; 1860 1861 fn unionLayout(self: *DeclGen, ty: Type) UnionLayout { 1862 const mod = self.module; 1863 const ip = &mod.intern_pool; 1864 const layout = ty.unionGetLayout(self.module); 1865 const union_obj = mod.typeToUnion(ty).?; 1866 1867 var union_layout = UnionLayout{ 1868 .has_payload = layout.payload_size != 0, 1869 .tag_size = @intCast(layout.tag_size), 1870 .tag_index = undefined, 1871 .payload_ty = undefined, 1872 .payload_size = undefined, 1873 .payload_index = undefined, 1874 .payload_padding_size = undefined, 1875 .payload_padding_index = undefined, 1876 .padding_size = @intCast(layout.padding), 1877 .padding_index = undefined, 1878 .total_fields = undefined, 1879 }; 1880 1881 if (union_layout.has_payload) { 1882 const most_aligned_field = layout.most_aligned_field; 1883 const most_aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[most_aligned_field]); 1884 union_layout.payload_ty = most_aligned_field_ty; 1885 union_layout.payload_size = @intCast(most_aligned_field_ty.abiSize(mod)); 1886 } else { 1887 union_layout.payload_size = 0; 1888 } 1889 1890 union_layout.payload_padding_size = @intCast(layout.payload_size - union_layout.payload_size); 1891 1892 const tag_first = layout.tag_align.compare(.gte, layout.payload_align); 1893 var field_index: u32 = 0; 1894 1895 if (union_layout.tag_size != 0 and tag_first) { 1896 union_layout.tag_index = field_index; 1897 field_index += 1; 1898 } 1899 1900 if (union_layout.payload_size != 0) { 1901 union_layout.payload_index = field_index; 1902 field_index += 1; 1903 } 1904 1905 if (union_layout.payload_padding_size != 0) { 1906 union_layout.payload_padding_index = field_index; 1907 field_index += 1; 1908 } 1909 1910 if (union_layout.tag_size != 0 and !tag_first) { 1911 union_layout.tag_index = field_index; 1912 field_index += 1; 1913 } 1914 1915 if (union_layout.padding_size != 0) { 1916 union_layout.padding_index = field_index; 1917 field_index += 1; 1918 } 1919 1920 union_layout.total_fields = field_index; 1921 1922 return union_layout; 1923 } 1924 1925 /// This structure is used as helper for element-wise operations. It is intended 1926 /// to be used with vectors, fake vectors (arrays) and single elements. 1927 const WipElementWise = struct { 1928 dg: *DeclGen, 1929 result_ty: Type, 1930 ty: Type, 1931 /// Always in direct representation. 1932 ty_id: IdRef, 1933 /// True if the input is an array type. 1934 is_array: bool, 1935 /// The element-wise operation should fill these results before calling finalize(). 1936 /// These should all be in **direct** representation! `finalize()` will convert 1937 /// them to indirect if required. 1938 results: []IdRef, 1939 1940 fn deinit(wip: *WipElementWise) void { 1941 wip.dg.gpa.free(wip.results); 1942 } 1943 1944 /// Utility function to extract the element at a particular index in an 1945 /// input array. This type is expected to be a fake vector (array) if `wip.is_array`, and 1946 /// a vector or scalar otherwise. 1947 fn elementAt(wip: WipElementWise, ty: Type, value: IdRef, index: usize) !IdRef { 1948 const mod = wip.dg.module; 1949 if (wip.is_array) { 1950 assert(ty.isVector(mod)); 1951 return try wip.dg.extractField(ty.childType(mod), value, @intCast(index)); 1952 } else { 1953 assert(index == 0); 1954 return value; 1955 } 1956 } 1957 1958 /// Turns the results of this WipElementWise into a result. This can be 1959 /// vectors, fake vectors (arrays) and single elements, depending on `result_ty`. 1960 /// After calling this function, this WIP is no longer usable. 1961 /// Results is in `direct` representation. 1962 fn finalize(wip: *WipElementWise) !IdRef { 1963 if (wip.is_array) { 1964 // Convert all the constituents to indirect, as required for the array. 1965 for (wip.results) |*result| { 1966 result.* = try wip.dg.convertToIndirect(wip.ty, result.*); 1967 } 1968 return try wip.dg.constructArray(wip.result_ty, wip.results); 1969 } else { 1970 return wip.results[0]; 1971 } 1972 } 1973 1974 /// Allocate a result id at a particular index, and return it. 1975 fn allocId(wip: *WipElementWise, index: usize) IdRef { 1976 assert(wip.is_array or index == 0); 1977 wip.results[index] = wip.dg.spv.allocId(); 1978 return wip.results[index]; 1979 } 1980 }; 1981 1982 /// Create a new element-wise operation. 1983 fn elementWise(self: *DeclGen, result_ty: Type, force_element_wise: bool) !WipElementWise { 1984 const mod = self.module; 1985 const is_array = result_ty.isVector(mod) and (!self.isVector(result_ty) or force_element_wise); 1986 const num_results = if (is_array) result_ty.vectorLen(mod) else 1; 1987 const results = try self.gpa.alloc(IdRef, num_results); 1988 @memset(results, undefined); 1989 1990 const ty = if (is_array) result_ty.scalarType(mod) else result_ty; 1991 const ty_id = try self.resolveType(ty, .direct); 1992 1993 return .{ 1994 .dg = self, 1995 .result_ty = result_ty, 1996 .ty = ty, 1997 .ty_id = ty_id, 1998 .is_array = is_array, 1999 .results = results, 2000 }; 2001 } 2002 2003 /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure. 2004 /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry- 2005 /// points. The test executor will then be able to invoke these to run the tests. 2006 /// Note that tests are lowered according to std.builtin.TestFn, which is `fn () anyerror!void`. 2007 /// (anyerror!void has the same layout as anyerror). 2008 /// Each test declaration generates a function like. 2009 /// %anyerror = OpTypeInt 0 16 2010 /// %p_invocation_globals_struct_ty = ... 2011 /// %p_anyerror = OpTypePointer CrossWorkgroup %anyerror 2012 /// %K = OpTypeFunction %void %p_invocation_globals_struct_ty %p_anyerror 2013 /// 2014 /// %test = OpFunction %void %K 2015 /// %p_invocation_globals = OpFunctionParameter p_invocation_globals_struct_ty 2016 /// %p_err = OpFunctionParameter %p_anyerror 2017 /// %lbl = OpLabel 2018 /// %result = OpFunctionCall %anyerror %func %p_invocation_globals 2019 /// OpStore %p_err %result 2020 /// OpFunctionEnd 2021 /// TODO is to also write out the error as a function call parameter, and to somehow fetch 2022 /// the name of an error in the text executor. 2023 fn generateTestEntryPoint(self: *DeclGen, name: []const u8, spv_test_decl_index: SpvModule.Decl.Index) !void { 2024 const anyerror_ty_id = try self.resolveType(Type.anyerror, .direct); 2025 const ptr_anyerror_ty = try self.module.ptrType(.{ 2026 .child = Type.anyerror.toIntern(), 2027 .flags = .{ .address_space = .global }, 2028 }); 2029 const ptr_anyerror_ty_id = try self.resolveType(ptr_anyerror_ty, .direct); 2030 const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty}); 2031 2032 const test_id = self.spv.declPtr(spv_test_decl_index).result_id; 2033 2034 const spv_decl_index = try self.spv.allocDecl(.func); 2035 const kernel_id = self.spv.declPtr(spv_decl_index).result_id; 2036 2037 const error_id = self.spv.allocId(); 2038 const p_error_id = self.spv.allocId(); 2039 2040 const section = &self.spv.sections.functions; 2041 try section.emit(self.spv.gpa, .OpFunction, .{ 2042 .id_result_type = try self.resolveType(Type.void, .direct), 2043 .id_result = kernel_id, 2044 .function_control = .{}, 2045 .function_type = kernel_proto_ty_id, 2046 }); 2047 try section.emit(self.spv.gpa, .OpFunctionParameter, .{ 2048 .id_result_type = ptr_anyerror_ty_id, 2049 .id_result = p_error_id, 2050 }); 2051 try section.emit(self.spv.gpa, .OpLabel, .{ 2052 .id_result = self.spv.allocId(), 2053 }); 2054 try section.emit(self.spv.gpa, .OpFunctionCall, .{ 2055 .id_result_type = anyerror_ty_id, 2056 .id_result = error_id, 2057 .function = test_id, 2058 }); 2059 // Note: Convert to direct not required. 2060 try section.emit(self.spv.gpa, .OpStore, .{ 2061 .pointer = p_error_id, 2062 .object = error_id, 2063 }); 2064 try section.emit(self.spv.gpa, .OpReturn, {}); 2065 try section.emit(self.spv.gpa, .OpFunctionEnd, {}); 2066 2067 try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index}); 2068 2069 // Just generate a quick other name because the intel runtime crashes when the entry- 2070 // point name is the same as a different OpName. 2071 const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name}); 2072 defer self.gpa.free(test_name); 2073 try self.spv.declareEntryPoint(spv_decl_index, test_name, .Kernel); 2074 } 2075 2076 fn genDecl(self: *DeclGen) !void { 2077 const mod = self.module; 2078 const ip = &mod.intern_pool; 2079 const decl = mod.declPtr(self.decl_index); 2080 const spv_decl_index = try self.object.resolveDecl(mod, self.decl_index); 2081 const result_id = self.spv.declPtr(spv_decl_index).result_id; 2082 2083 switch (self.spv.declPtr(spv_decl_index).kind) { 2084 .func => { 2085 assert(decl.typeOf(mod).zigTypeTag(mod) == .Fn); 2086 const fn_info = mod.typeToFunc(decl.typeOf(mod)).?; 2087 const return_ty_id = try self.resolveFnReturnType(Type.fromInterned(fn_info.return_type)); 2088 2089 const prototype_ty_id = try self.resolveType(decl.typeOf(mod), .direct); 2090 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ 2091 .id_result_type = return_ty_id, 2092 .id_result = result_id, 2093 .function_control = switch (fn_info.cc) { 2094 .Inline => .{ .Inline = true }, 2095 else => .{}, 2096 }, 2097 .function_type = prototype_ty_id, 2098 }); 2099 2100 comptime assert(zig_call_abi_ver == 3); 2101 try self.args.ensureUnusedCapacity(self.gpa, fn_info.param_types.len); 2102 for (fn_info.param_types.get(ip)) |param_ty_index| { 2103 const param_ty = Type.fromInterned(param_ty_index); 2104 if (!param_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; 2105 2106 const param_type_id = try self.resolveType(param_ty, .direct); 2107 const arg_result_id = self.spv.allocId(); 2108 try self.func.prologue.emit(self.spv.gpa, .OpFunctionParameter, .{ 2109 .id_result_type = param_type_id, 2110 .id_result = arg_result_id, 2111 }); 2112 self.args.appendAssumeCapacity(arg_result_id); 2113 } 2114 2115 // TODO: This could probably be done in a better way... 2116 const root_block_id = self.spv.allocId(); 2117 2118 // The root block of a function declaration should appear before OpVariable instructions, 2119 // so it is generated into the function's prologue. 2120 try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{ 2121 .id_result = root_block_id, 2122 }); 2123 self.current_block_label = root_block_id; 2124 2125 const main_body = self.air.getMainBody(); 2126 switch (self.control_flow) { 2127 .structured => { 2128 _ = try self.genStructuredBody(.selection, main_body); 2129 // We always expect paths to here to end, but we still need the block 2130 // to act as a dummy merge block. 2131 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); 2132 }, 2133 .unstructured => { 2134 try self.genBody(main_body); 2135 }, 2136 } 2137 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); 2138 // Append the actual code into the functions section. 2139 try self.spv.addFunction(spv_decl_index, self.func); 2140 2141 const fqn = try decl.fullyQualifiedName(self.module); 2142 try self.spv.debugName(result_id, fqn.toSlice(ip)); 2143 2144 // Temporarily generate a test kernel declaration if this is a test function. 2145 if (self.module.test_functions.contains(self.decl_index)) { 2146 try self.generateTestEntryPoint(fqn.toSlice(ip), spv_decl_index); 2147 } 2148 }, 2149 .global => { 2150 const maybe_init_val: ?Value = blk: { 2151 if (decl.val.getVariable(mod)) |payload| { 2152 if (payload.is_extern) break :blk null; 2153 break :blk Value.fromInterned(payload.init); 2154 } 2155 break :blk decl.val; 2156 }; 2157 assert(maybe_init_val == null); // TODO 2158 2159 const final_storage_class = self.spvStorageClass(decl.@"addrspace"); 2160 assert(final_storage_class != .Generic); // These should be instance globals 2161 2162 const ptr_ty_id = try self.ptrType(decl.typeOf(mod), final_storage_class); 2163 2164 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{ 2165 .id_result_type = ptr_ty_id, 2166 .id_result = result_id, 2167 .storage_class = final_storage_class, 2168 }); 2169 2170 const fqn = try decl.fullyQualifiedName(self.module); 2171 try self.spv.debugName(result_id, fqn.toSlice(ip)); 2172 try self.spv.declareDeclDeps(spv_decl_index, &.{}); 2173 }, 2174 .invocation_global => { 2175 const maybe_init_val: ?Value = blk: { 2176 if (decl.val.getVariable(mod)) |payload| { 2177 if (payload.is_extern) break :blk null; 2178 break :blk Value.fromInterned(payload.init); 2179 } 2180 break :blk decl.val; 2181 }; 2182 2183 try self.spv.declareDeclDeps(spv_decl_index, &.{}); 2184 2185 const ptr_ty_id = try self.ptrType(decl.typeOf(mod), .Function); 2186 2187 if (maybe_init_val) |init_val| { 2188 // TODO: Combine with resolveAnonDecl? 2189 const initializer_proto_ty_id = try self.functionType(Type.void, &.{}); 2190 2191 const initializer_id = self.spv.allocId(); 2192 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ 2193 .id_result_type = try self.resolveType(Type.void, .direct), 2194 .id_result = initializer_id, 2195 .function_control = .{}, 2196 .function_type = initializer_proto_ty_id, 2197 }); 2198 2199 const root_block_id = self.spv.allocId(); 2200 try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{ 2201 .id_result = root_block_id, 2202 }); 2203 self.current_block_label = root_block_id; 2204 2205 const val_id = try self.constant(decl.typeOf(mod), init_val, .indirect); 2206 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 2207 .pointer = result_id, 2208 .object = val_id, 2209 }); 2210 2211 try self.func.body.emit(self.spv.gpa, .OpReturn, {}); 2212 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); 2213 try self.spv.addFunction(spv_decl_index, self.func); 2214 2215 const fqn = try decl.fullyQualifiedName(self.module); 2216 try self.spv.debugNameFmt(initializer_id, "initializer of {}", .{fqn.fmt(ip)}); 2217 2218 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpExtInst, .{ 2219 .id_result_type = ptr_ty_id, 2220 .id_result = result_id, 2221 .set = try self.spv.importInstructionSet(.zig), 2222 .instruction = .{ .inst = 0 }, // TODO: Put this definition somewhere... 2223 .id_ref_4 = &.{initializer_id}, 2224 }); 2225 } else { 2226 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpExtInst, .{ 2227 .id_result_type = ptr_ty_id, 2228 .id_result = result_id, 2229 .set = try self.spv.importInstructionSet(.zig), 2230 .instruction = .{ .inst = 0 }, // TODO: Put this definition somewhere... 2231 .id_ref_4 = &.{}, 2232 }); 2233 } 2234 }, 2235 } 2236 } 2237 2238 fn intFromBool(self: *DeclGen, ty: Type, condition_id: IdRef) !IdRef { 2239 const zero_id = try self.constInt(ty, 0, .direct); 2240 const one_id = try self.constInt(ty, 1, .direct); 2241 const result_id = self.spv.allocId(); 2242 try self.func.body.emit(self.spv.gpa, .OpSelect, .{ 2243 .id_result_type = try self.resolveType(ty, .direct), 2244 .id_result = result_id, 2245 .condition = condition_id, 2246 .object_1 = one_id, 2247 .object_2 = zero_id, 2248 }); 2249 return result_id; 2250 } 2251 2252 /// Convert representation from indirect (in memory) to direct (in 'register') 2253 /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct). 2254 fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { 2255 const mod = self.module; 2256 return switch (ty.zigTypeTag(mod)) { 2257 .Bool => blk: { 2258 const result_id = self.spv.allocId(); 2259 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ 2260 .id_result_type = try self.resolveType(Type.bool, .direct), 2261 .id_result = result_id, 2262 .operand_1 = operand_id, 2263 .operand_2 = try self.constBool(false, .indirect), 2264 }); 2265 break :blk result_id; 2266 }, 2267 else => operand_id, 2268 }; 2269 } 2270 2271 /// Convert representation from direct (in 'register) to direct (in memory) 2272 /// This converts the argument type from resolveType(ty, .direct) to resolveType(ty, .indirect). 2273 fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { 2274 const mod = self.module; 2275 return switch (ty.zigTypeTag(mod)) { 2276 .Bool => try self.intFromBool(Type.u1, operand_id), 2277 else => operand_id, 2278 }; 2279 } 2280 2281 fn extractField(self: *DeclGen, result_ty: Type, object: IdRef, field: u32) !IdRef { 2282 const result_ty_id = try self.resolveType(result_ty, .indirect); 2283 const result_id = self.spv.allocId(); 2284 const indexes = [_]u32{field}; 2285 try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ 2286 .id_result_type = result_ty_id, 2287 .id_result = result_id, 2288 .composite = object, 2289 .indexes = &indexes, 2290 }); 2291 // Convert bools; direct structs have their field types as indirect values. 2292 return try self.convertToDirect(result_ty, result_id); 2293 } 2294 2295 const MemoryOptions = struct { 2296 is_volatile: bool = false, 2297 }; 2298 2299 fn load(self: *DeclGen, value_ty: Type, ptr_id: IdRef, options: MemoryOptions) !IdRef { 2300 const indirect_value_ty_id = try self.resolveType(value_ty, .indirect); 2301 const result_id = self.spv.allocId(); 2302 const access = spec.MemoryAccess.Extended{ 2303 .Volatile = options.is_volatile, 2304 }; 2305 try self.func.body.emit(self.spv.gpa, .OpLoad, .{ 2306 .id_result_type = indirect_value_ty_id, 2307 .id_result = result_id, 2308 .pointer = ptr_id, 2309 .memory_access = access, 2310 }); 2311 return try self.convertToDirect(value_ty, result_id); 2312 } 2313 2314 fn store(self: *DeclGen, value_ty: Type, ptr_id: IdRef, value_id: IdRef, options: MemoryOptions) !void { 2315 const indirect_value_id = try self.convertToIndirect(value_ty, value_id); 2316 const access = spec.MemoryAccess.Extended{ 2317 .Volatile = options.is_volatile, 2318 }; 2319 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 2320 .pointer = ptr_id, 2321 .object = indirect_value_id, 2322 .memory_access = access, 2323 }); 2324 } 2325 2326 fn genBody(self: *DeclGen, body: []const Air.Inst.Index) Error!void { 2327 for (body) |inst| { 2328 try self.genInst(inst); 2329 } 2330 } 2331 2332 fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void { 2333 const mod = self.module; 2334 const ip = &mod.intern_pool; 2335 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) 2336 return; 2337 2338 const air_tags = self.air.instructions.items(.tag); 2339 const maybe_result_id: ?IdRef = switch (air_tags[@intFromEnum(inst)]) { 2340 // zig fmt: off 2341 .add, .add_wrap, .add_optimized => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd), 2342 .sub, .sub_wrap, .sub_optimized => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub), 2343 .mul, .mul_wrap, .mul_optimized => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul), 2344 2345 2346 .abs => try self.airAbs(inst), 2347 .floor => try self.airFloor(inst), 2348 2349 .div_floor => try self.airDivFloor(inst), 2350 2351 .div_float, 2352 .div_float_optimized, 2353 .div_trunc, 2354 .div_trunc_optimized => try self.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv), 2355 .rem, .rem_optimized => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem), 2356 .mod, .mod_optimized => try self.airArithOp(inst, .OpFMod, .OpSMod, .OpSMod), 2357 2358 2359 .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan), 2360 .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan), 2361 .mul_with_overflow => try self.airMulOverflow(inst), 2362 .shl_with_overflow => try self.airShlOverflow(inst), 2363 2364 .mul_add => try self.airMulAdd(inst), 2365 2366 .ctz => try self.airClzCtz(inst, .ctz), 2367 .clz => try self.airClzCtz(inst, .clz), 2368 2369 .splat => try self.airSplat(inst), 2370 .reduce, .reduce_optimized => try self.airReduce(inst), 2371 .shuffle => try self.airShuffle(inst), 2372 2373 .ptr_add => try self.airPtrAdd(inst), 2374 .ptr_sub => try self.airPtrSub(inst), 2375 2376 .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), 2377 .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr), 2378 .xor => try self.airBinOpSimple(inst, .OpBitwiseXor), 2379 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd), 2380 .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr), 2381 2382 .shl, .shl_exact => try self.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical), 2383 .shr, .shr_exact => try self.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic), 2384 2385 .min => try self.airMinMax(inst, .lt), 2386 .max => try self.airMinMax(inst, .gt), 2387 2388 .bitcast => try self.airBitCast(inst), 2389 .intcast, .trunc => try self.airIntCast(inst), 2390 .int_from_ptr => try self.airIntFromPtr(inst), 2391 .float_from_int => try self.airFloatFromInt(inst), 2392 .int_from_float => try self.airIntFromFloat(inst), 2393 .int_from_bool => try self.airIntFromBool(inst), 2394 .fpext, .fptrunc => try self.airFloatCast(inst), 2395 .not => try self.airNot(inst), 2396 2397 .array_to_slice => try self.airArrayToSlice(inst), 2398 .slice => try self.airSlice(inst), 2399 .aggregate_init => try self.airAggregateInit(inst), 2400 .memcpy => return self.airMemcpy(inst), 2401 2402 .slice_ptr => try self.airSliceField(inst, 0), 2403 .slice_len => try self.airSliceField(inst, 1), 2404 .slice_elem_ptr => try self.airSliceElemPtr(inst), 2405 .slice_elem_val => try self.airSliceElemVal(inst), 2406 .ptr_elem_ptr => try self.airPtrElemPtr(inst), 2407 .ptr_elem_val => try self.airPtrElemVal(inst), 2408 .array_elem_val => try self.airArrayElemVal(inst), 2409 2410 .vector_store_elem => return self.airVectorStoreElem(inst), 2411 2412 .set_union_tag => return self.airSetUnionTag(inst), 2413 .get_union_tag => try self.airGetUnionTag(inst), 2414 .union_init => try self.airUnionInit(inst), 2415 2416 .struct_field_val => try self.airStructFieldVal(inst), 2417 .field_parent_ptr => try self.airFieldParentPtr(inst), 2418 2419 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), 2420 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), 2421 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), 2422 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), 2423 2424 .cmp_eq => try self.airCmp(inst, .eq), 2425 .cmp_neq => try self.airCmp(inst, .neq), 2426 .cmp_gt => try self.airCmp(inst, .gt), 2427 .cmp_gte => try self.airCmp(inst, .gte), 2428 .cmp_lt => try self.airCmp(inst, .lt), 2429 .cmp_lte => try self.airCmp(inst, .lte), 2430 .cmp_vector => try self.airVectorCmp(inst), 2431 2432 .arg => self.airArg(), 2433 .alloc => try self.airAlloc(inst), 2434 // TODO: We probably need to have a special implementation of this for the C abi. 2435 .ret_ptr => try self.airAlloc(inst), 2436 .block => try self.airBlock(inst), 2437 2438 .load => try self.airLoad(inst), 2439 .store, .store_safe => return self.airStore(inst), 2440 2441 .br => return self.airBr(inst), 2442 .breakpoint => return, 2443 .cond_br => return self.airCondBr(inst), 2444 .loop => return self.airLoop(inst), 2445 .ret => return self.airRet(inst), 2446 .ret_safe => return self.airRet(inst), // TODO 2447 .ret_load => return self.airRetLoad(inst), 2448 .@"try" => try self.airTry(inst), 2449 .switch_br => return self.airSwitchBr(inst), 2450 .unreach, .trap => return self.airUnreach(), 2451 2452 .dbg_stmt => return self.airDbgStmt(inst), 2453 .dbg_inline_block => try self.airDbgInlineBlock(inst), 2454 .dbg_var_ptr, .dbg_var_val => return self.airDbgVar(inst), 2455 2456 .unwrap_errunion_err => try self.airErrUnionErr(inst), 2457 .unwrap_errunion_payload => try self.airErrUnionPayload(inst), 2458 .wrap_errunion_err => try self.airWrapErrUnionErr(inst), 2459 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), 2460 2461 .is_null => try self.airIsNull(inst, false, .is_null), 2462 .is_non_null => try self.airIsNull(inst, false, .is_non_null), 2463 .is_null_ptr => try self.airIsNull(inst, true, .is_null), 2464 .is_non_null_ptr => try self.airIsNull(inst, true, .is_non_null), 2465 .is_err => try self.airIsErr(inst, .is_err), 2466 .is_non_err => try self.airIsErr(inst, .is_non_err), 2467 2468 .optional_payload => try self.airUnwrapOptional(inst), 2469 .optional_payload_ptr => try self.airUnwrapOptionalPtr(inst), 2470 .wrap_optional => try self.airWrapOptional(inst), 2471 2472 .assembly => try self.airAssembly(inst), 2473 2474 .call => try self.airCall(inst, .auto), 2475 .call_always_tail => try self.airCall(inst, .always_tail), 2476 .call_never_tail => try self.airCall(inst, .never_tail), 2477 .call_never_inline => try self.airCall(inst, .never_inline), 2478 // zig fmt: on 2479 2480 else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}), 2481 }; 2482 2483 const result_id = maybe_result_id orelse return; 2484 try self.inst_results.putNoClobber(self.gpa, inst, result_id); 2485 } 2486 2487 fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef { 2488 var wip = try self.elementWise(ty, false); 2489 defer wip.deinit(); 2490 for (0..wip.results.len) |i| { 2491 try self.func.body.emit(self.spv.gpa, opcode, .{ 2492 .id_result_type = wip.ty_id, 2493 .id_result = wip.allocId(i), 2494 .operand_1 = try wip.elementAt(ty, lhs_id, i), 2495 .operand_2 = try wip.elementAt(ty, rhs_id, i), 2496 }); 2497 } 2498 return try wip.finalize(); 2499 } 2500 2501 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef { 2502 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 2503 const lhs_id = try self.resolve(bin_op.lhs); 2504 const rhs_id = try self.resolve(bin_op.rhs); 2505 const ty = self.typeOf(bin_op.lhs); 2506 2507 return try self.binOpSimple(ty, lhs_id, rhs_id, opcode); 2508 } 2509 2510 fn airShift(self: *DeclGen, inst: Air.Inst.Index, comptime unsigned: Opcode, comptime signed: Opcode) !?IdRef { 2511 const mod = self.module; 2512 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 2513 const lhs_id = try self.resolve(bin_op.lhs); 2514 const rhs_id = try self.resolve(bin_op.rhs); 2515 2516 const result_ty = self.typeOfIndex(inst); 2517 const shift_ty = self.typeOf(bin_op.rhs); 2518 const scalar_result_ty_id = try self.resolveType(result_ty.scalarType(mod), .direct); 2519 const scalar_shift_ty_id = try self.resolveType(shift_ty.scalarType(mod), .direct); 2520 2521 const info = self.arithmeticTypeInfo(result_ty); 2522 switch (info.class) { 2523 .composite_integer => return self.todo("shift ops for composite integers", .{}), 2524 .integer, .strange_integer => {}, 2525 .float, .bool => unreachable, 2526 } 2527 2528 var wip = try self.elementWise(result_ty, false); 2529 defer wip.deinit(); 2530 for (wip.results, 0..) |*result_id, i| { 2531 const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); 2532 const rhs_elem_id = try wip.elementAt(shift_ty, rhs_id, i); 2533 2534 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, 2535 // so just manually upcast it if required. 2536 const shift_id = if (scalar_shift_ty_id != scalar_result_ty_id) blk: { 2537 const shift_id = self.spv.allocId(); 2538 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ 2539 .id_result_type = wip.ty_id, 2540 .id_result = shift_id, 2541 .unsigned_value = rhs_elem_id, 2542 }); 2543 break :blk shift_id; 2544 } else rhs_elem_id; 2545 2546 const value_id = self.spv.allocId(); 2547 const args = .{ 2548 .id_result_type = wip.ty_id, 2549 .id_result = value_id, 2550 .base = lhs_elem_id, 2551 .shift = shift_id, 2552 }; 2553 2554 if (result_ty.isSignedInt(mod)) { 2555 try self.func.body.emit(self.spv.gpa, signed, args); 2556 } else { 2557 try self.func.body.emit(self.spv.gpa, unsigned, args); 2558 } 2559 2560 result_id.* = try self.normalize(wip.ty, value_id, info); 2561 } 2562 return try wip.finalize(); 2563 } 2564 2565 fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef { 2566 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 2567 const lhs_id = try self.resolve(bin_op.lhs); 2568 const rhs_id = try self.resolve(bin_op.rhs); 2569 const result_ty = self.typeOfIndex(inst); 2570 2571 return try self.minMax(result_ty, op, lhs_id, rhs_id); 2572 } 2573 2574 fn minMax(self: *DeclGen, result_ty: Type, op: std.math.CompareOperator, lhs_id: IdRef, rhs_id: IdRef) !IdRef { 2575 const info = self.arithmeticTypeInfo(result_ty); 2576 const target = self.getTarget(); 2577 2578 const use_backup_codegen = target.os.tag == .opencl and info.class != .float; 2579 var wip = try self.elementWise(result_ty, use_backup_codegen); 2580 defer wip.deinit(); 2581 2582 for (wip.results, 0..) |*result_id, i| { 2583 const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); 2584 const rhs_elem_id = try wip.elementAt(result_ty, rhs_id, i); 2585 2586 if (use_backup_codegen) { 2587 const cmp_id = try self.cmp(op, Type.bool, wip.ty, lhs_elem_id, rhs_elem_id); 2588 result_id.* = self.spv.allocId(); 2589 try self.func.body.emit(self.spv.gpa, .OpSelect, .{ 2590 .id_result_type = wip.ty_id, 2591 .id_result = result_id.*, 2592 .condition = cmp_id, 2593 .object_1 = lhs_elem_id, 2594 .object_2 = rhs_elem_id, 2595 }); 2596 } else { 2597 const ext_inst: Word = switch (target.os.tag) { 2598 .opencl => switch (op) { 2599 .lt => 28, // fmin 2600 .gt => 27, // fmax 2601 else => unreachable, 2602 }, 2603 .vulkan => switch (info.class) { 2604 .float => switch (op) { 2605 .lt => 37, // FMin 2606 .gt => 40, // FMax 2607 else => unreachable, 2608 }, 2609 .integer, .strange_integer => switch (info.signedness) { 2610 .signed => switch (op) { 2611 .lt => 39, // SMin 2612 .gt => 42, // SMax 2613 else => unreachable, 2614 }, 2615 .unsigned => switch (op) { 2616 .lt => 38, // UMin 2617 .gt => 41, // UMax 2618 else => unreachable, 2619 }, 2620 }, 2621 .composite_integer => unreachable, // TODO 2622 .bool => unreachable, 2623 }, 2624 else => unreachable, 2625 }; 2626 const set_id = switch (target.os.tag) { 2627 .opencl => try self.spv.importInstructionSet(.@"OpenCL.std"), 2628 .vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"), 2629 else => unreachable, 2630 }; 2631 2632 result_id.* = self.spv.allocId(); 2633 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{ 2634 .id_result_type = wip.ty_id, 2635 .id_result = result_id.*, 2636 .set = set_id, 2637 .instruction = .{ .inst = ext_inst }, 2638 .id_ref_4 = &.{ lhs_elem_id, rhs_elem_id }, 2639 }); 2640 } 2641 } 2642 return wip.finalize(); 2643 } 2644 2645 /// This function normalizes values to a canonical representation 2646 /// after some arithmetic operation. This mostly consists of wrapping 2647 /// behavior for strange integers: 2648 /// - Unsigned integers are bitwise masked with a mask that only passes 2649 /// the valid bits through. 2650 /// - Signed integers are also sign extended if they are negative. 2651 /// All other values are returned unmodified (this makes strange integer 2652 /// wrapping easier to use in generic operations). 2653 fn normalize(self: *DeclGen, ty: Type, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef { 2654 switch (info.class) { 2655 .integer, .bool, .float => return value_id, 2656 .composite_integer => unreachable, // TODO 2657 .strange_integer => switch (info.signedness) { 2658 .unsigned => { 2659 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; 2660 const result_id = self.spv.allocId(); 2661 const mask_id = try self.constInt(ty, mask_value, .direct); 2662 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{ 2663 .id_result_type = try self.resolveType(ty, .direct), 2664 .id_result = result_id, 2665 .operand_1 = value_id, 2666 .operand_2 = mask_id, 2667 }); 2668 return result_id; 2669 }, 2670 .signed => { 2671 // Shift left and right so that we can copy the sight bit that way. 2672 const shift_amt_id = try self.constInt(ty, info.backing_bits - info.bits, .direct); 2673 const left_id = self.spv.allocId(); 2674 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ 2675 .id_result_type = try self.resolveType(ty, .direct), 2676 .id_result = left_id, 2677 .base = value_id, 2678 .shift = shift_amt_id, 2679 }); 2680 const right_id = self.spv.allocId(); 2681 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ 2682 .id_result_type = try self.resolveType(ty, .direct), 2683 .id_result = right_id, 2684 .base = left_id, 2685 .shift = shift_amt_id, 2686 }); 2687 return right_id; 2688 }, 2689 }, 2690 } 2691 } 2692 2693 fn airDivFloor(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2694 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 2695 const lhs_id = try self.resolve(bin_op.lhs); 2696 const rhs_id = try self.resolve(bin_op.rhs); 2697 const ty = self.typeOfIndex(inst); 2698 const ty_id = try self.resolveType(ty, .direct); 2699 const info = self.arithmeticTypeInfo(ty); 2700 switch (info.class) { 2701 .composite_integer => unreachable, // TODO 2702 .integer, .strange_integer => { 2703 const zero_id = try self.constInt(ty, 0, .direct); 2704 const one_id = try self.constInt(ty, 1, .direct); 2705 2706 // (a ^ b) > 0 2707 const bin_bitwise_id = try self.binOpSimple(ty, lhs_id, rhs_id, .OpBitwiseXor); 2708 const is_positive_id = try self.cmp(.gt, Type.bool, ty, bin_bitwise_id, zero_id); 2709 2710 // a / b 2711 const positive_div_id = try self.arithOp(ty, lhs_id, rhs_id, .OpFDiv, .OpSDiv, .OpUDiv); 2712 2713 // - (abs(a) + abs(b) - 1) / abs(b) 2714 const lhs_abs = try self.abs(ty, ty, lhs_id); 2715 const rhs_abs = try self.abs(ty, ty, rhs_id); 2716 const negative_div_lhs = try self.arithOp( 2717 ty, 2718 try self.arithOp(ty, lhs_abs, rhs_abs, .OpFAdd, .OpIAdd, .OpIAdd), 2719 one_id, 2720 .OpFSub, 2721 .OpISub, 2722 .OpISub, 2723 ); 2724 const negative_div_id = try self.arithOp(ty, negative_div_lhs, rhs_abs, .OpFDiv, .OpSDiv, .OpUDiv); 2725 const negated_negative_div_id = self.spv.allocId(); 2726 try self.func.body.emit(self.spv.gpa, .OpSNegate, .{ 2727 .id_result_type = ty_id, 2728 .id_result = negated_negative_div_id, 2729 .operand = negative_div_id, 2730 }); 2731 2732 const result_id = self.spv.allocId(); 2733 try self.func.body.emit(self.spv.gpa, .OpSelect, .{ 2734 .id_result_type = ty_id, 2735 .id_result = result_id, 2736 .condition = is_positive_id, 2737 .object_1 = positive_div_id, 2738 .object_2 = negated_negative_div_id, 2739 }); 2740 return result_id; 2741 }, 2742 .float => { 2743 const div_id = try self.arithOp(ty, lhs_id, rhs_id, .OpFDiv, .OpSDiv, .OpUDiv); 2744 return try self.floor(ty, div_id); 2745 }, 2746 .bool => unreachable, 2747 } 2748 } 2749 2750 fn airFloor(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2751 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2752 const operand_id = try self.resolve(un_op); 2753 const result_ty = self.typeOfIndex(inst); 2754 return try self.floor(result_ty, operand_id); 2755 } 2756 2757 fn floor(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { 2758 const target = self.getTarget(); 2759 const ty_id = try self.resolveType(ty, .direct); 2760 const ext_inst: Word = switch (target.os.tag) { 2761 .opencl => 25, 2762 .vulkan => 8, 2763 else => unreachable, 2764 }; 2765 const set_id = switch (target.os.tag) { 2766 .opencl => try self.spv.importInstructionSet(.@"OpenCL.std"), 2767 .vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"), 2768 else => unreachable, 2769 }; 2770 2771 const result_id = self.spv.allocId(); 2772 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{ 2773 .id_result_type = ty_id, 2774 .id_result = result_id, 2775 .set = set_id, 2776 .instruction = .{ .inst = ext_inst }, 2777 .id_ref_4 = &.{operand_id}, 2778 }); 2779 return result_id; 2780 } 2781 2782 fn airArithOp( 2783 self: *DeclGen, 2784 inst: Air.Inst.Index, 2785 comptime fop: Opcode, 2786 comptime sop: Opcode, 2787 comptime uop: Opcode, 2788 ) !?IdRef { 2789 // LHS and RHS are guaranteed to have the same type, and AIR guarantees 2790 // the result to be the same as the LHS and RHS, which matches SPIR-V. 2791 const ty = self.typeOfIndex(inst); 2792 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 2793 const lhs_id = try self.resolve(bin_op.lhs); 2794 const rhs_id = try self.resolve(bin_op.rhs); 2795 2796 assert(self.typeOf(bin_op.lhs).eql(ty, self.module)); 2797 assert(self.typeOf(bin_op.rhs).eql(ty, self.module)); 2798 2799 return try self.arithOp(ty, lhs_id, rhs_id, fop, sop, uop); 2800 } 2801 2802 fn arithOp( 2803 self: *DeclGen, 2804 ty: Type, 2805 lhs_id: IdRef, 2806 rhs_id: IdRef, 2807 comptime fop: Opcode, 2808 comptime sop: Opcode, 2809 comptime uop: Opcode, 2810 ) !IdRef { 2811 // Binary operations are generally applicable to both scalar and vector operations 2812 // in SPIR-V, but int and float versions of operations require different opcodes. 2813 const info = self.arithmeticTypeInfo(ty); 2814 2815 const opcode_index: usize = switch (info.class) { 2816 .composite_integer => { 2817 return self.todo("binary operations for composite integers", .{}); 2818 }, 2819 .integer, .strange_integer => switch (info.signedness) { 2820 .signed => 1, 2821 .unsigned => 2, 2822 }, 2823 .float => 0, 2824 .bool => unreachable, 2825 }; 2826 2827 var wip = try self.elementWise(ty, false); 2828 defer wip.deinit(); 2829 for (wip.results, 0..) |*result_id, i| { 2830 const lhs_elem_id = try wip.elementAt(ty, lhs_id, i); 2831 const rhs_elem_id = try wip.elementAt(ty, rhs_id, i); 2832 2833 const value_id = self.spv.allocId(); 2834 const operands = .{ 2835 .id_result_type = wip.ty_id, 2836 .id_result = value_id, 2837 .operand_1 = lhs_elem_id, 2838 .operand_2 = rhs_elem_id, 2839 }; 2840 2841 switch (opcode_index) { 2842 0 => try self.func.body.emit(self.spv.gpa, fop, operands), 2843 1 => try self.func.body.emit(self.spv.gpa, sop, operands), 2844 2 => try self.func.body.emit(self.spv.gpa, uop, operands), 2845 else => unreachable, 2846 } 2847 2848 // TODO: Trap on overflow? Probably going to be annoying. 2849 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. 2850 result_id.* = try self.normalize(wip.ty, value_id, info); 2851 } 2852 2853 return try wip.finalize(); 2854 } 2855 2856 fn airAbs(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2857 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 2858 const operand_id = try self.resolve(ty_op.operand); 2859 // Note: operand_ty may be signed, while ty is always unsigned! 2860 const operand_ty = self.typeOf(ty_op.operand); 2861 const result_ty = self.typeOfIndex(inst); 2862 return try self.abs(result_ty, operand_ty, operand_id); 2863 } 2864 2865 fn abs(self: *DeclGen, result_ty: Type, operand_ty: Type, operand_id: IdRef) !IdRef { 2866 const target = self.getTarget(); 2867 const operand_info = self.arithmeticTypeInfo(operand_ty); 2868 2869 var wip = try self.elementWise(result_ty, false); 2870 defer wip.deinit(); 2871 2872 for (wip.results, 0..) |*result_id, i| { 2873 const elem_id = try wip.elementAt(operand_ty, operand_id, i); 2874 2875 const ext_inst: Word = switch (target.os.tag) { 2876 .opencl => switch (operand_info.class) { 2877 .float => 23, // fabs 2878 .integer, .strange_integer => switch (operand_info.signedness) { 2879 .signed => 141, // s_abs 2880 .unsigned => 201, // u_abs 2881 }, 2882 .composite_integer => unreachable, // TODO 2883 .bool => unreachable, 2884 }, 2885 .vulkan => switch (operand_info.class) { 2886 .float => 4, // FAbs 2887 .integer, .strange_integer => 5, // SAbs 2888 .composite_integer => unreachable, // TODO 2889 .bool => unreachable, 2890 }, 2891 else => unreachable, 2892 }; 2893 const set_id = switch (target.os.tag) { 2894 .opencl => try self.spv.importInstructionSet(.@"OpenCL.std"), 2895 .vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"), 2896 else => unreachable, 2897 }; 2898 2899 result_id.* = self.spv.allocId(); 2900 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{ 2901 .id_result_type = wip.ty_id, 2902 .id_result = result_id.*, 2903 .set = set_id, 2904 .instruction = .{ .inst = ext_inst }, 2905 .id_ref_4 = &.{elem_id}, 2906 }); 2907 } 2908 return try wip.finalize(); 2909 } 2910 2911 fn airAddSubOverflow( 2912 self: *DeclGen, 2913 inst: Air.Inst.Index, 2914 comptime add: Opcode, 2915 comptime ucmp: Opcode, 2916 comptime scmp: Opcode, 2917 ) !?IdRef { 2918 const mod = self.module; 2919 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 2920 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 2921 const lhs = try self.resolve(extra.lhs); 2922 const rhs = try self.resolve(extra.rhs); 2923 2924 const result_ty = self.typeOfIndex(inst); 2925 const operand_ty = self.typeOf(extra.lhs); 2926 const ov_ty = result_ty.structFieldType(1, self.module); 2927 2928 const bool_ty_id = try self.resolveType(Type.bool, .direct); 2929 const cmp_ty_id = if (self.isVector(operand_ty)) 2930 // TODO: Resolving a vector type with .direct should return a SPIR-V vector 2931 try self.spv.vectorType(operand_ty.vectorLen(mod), try self.resolveType(Type.bool, .direct)) 2932 else 2933 bool_ty_id; 2934 2935 const info = self.arithmeticTypeInfo(operand_ty); 2936 switch (info.class) { 2937 .composite_integer => return self.todo("overflow ops for composite integers", .{}), 2938 .strange_integer, .integer => {}, 2939 .float, .bool => unreachable, 2940 } 2941 2942 var wip_result = try self.elementWise(operand_ty, false); 2943 defer wip_result.deinit(); 2944 var wip_ov = try self.elementWise(ov_ty, false); 2945 defer wip_ov.deinit(); 2946 for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { 2947 const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); 2948 const rhs_elem_id = try wip_result.elementAt(operand_ty, rhs, i); 2949 2950 // Normalize both so that we can properly check for overflow 2951 const value_id = self.spv.allocId(); 2952 2953 try self.func.body.emit(self.spv.gpa, add, .{ 2954 .id_result_type = wip_result.ty_id, 2955 .id_result = value_id, 2956 .operand_1 = lhs_elem_id, 2957 .operand_2 = rhs_elem_id, 2958 }); 2959 2960 // Normalize the result so that the comparisons go well 2961 result_id.* = try self.normalize(wip_result.ty, value_id, info); 2962 2963 const overflowed_id = switch (info.signedness) { 2964 .unsigned => blk: { 2965 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which. 2966 // For subtraction the conditions need to be swapped. 2967 const overflowed_id = self.spv.allocId(); 2968 try self.func.body.emit(self.spv.gpa, ucmp, .{ 2969 .id_result_type = cmp_ty_id, 2970 .id_result = overflowed_id, 2971 .operand_1 = result_id.*, 2972 .operand_2 = lhs_elem_id, 2973 }); 2974 break :blk overflowed_id; 2975 }, 2976 .signed => blk: { 2977 // lhs - rhs 2978 // For addition, overflow happened if: 2979 // - rhs is negative and value > lhs 2980 // - rhs is positive and value < lhs 2981 // This can be shortened to: 2982 // (rhs < 0 and value > lhs) or (rhs >= 0 and value <= lhs) 2983 // = (rhs < 0) == (value > lhs) 2984 // = (rhs < 0) == (lhs < value) 2985 // Note that signed overflow is also wrapping in spir-v. 2986 // For subtraction, overflow happened if: 2987 // - rhs is negative and value < lhs 2988 // - rhs is positive and value > lhs 2989 // This can be shortened to: 2990 // (rhs < 0 and value < lhs) or (rhs >= 0 and value >= lhs) 2991 // = (rhs < 0) == (value < lhs) 2992 // = (rhs < 0) == (lhs > value) 2993 2994 const rhs_lt_zero_id = self.spv.allocId(); 2995 const zero_id = try self.constInt(wip_result.ty, 0, .direct); 2996 try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ 2997 .id_result_type = cmp_ty_id, 2998 .id_result = rhs_lt_zero_id, 2999 .operand_1 = rhs_elem_id, 3000 .operand_2 = zero_id, 3001 }); 3002 3003 const value_gt_lhs_id = self.spv.allocId(); 3004 try self.func.body.emit(self.spv.gpa, scmp, .{ 3005 .id_result_type = cmp_ty_id, 3006 .id_result = value_gt_lhs_id, 3007 .operand_1 = lhs_elem_id, 3008 .operand_2 = result_id.*, 3009 }); 3010 3011 const overflowed_id = self.spv.allocId(); 3012 try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{ 3013 .id_result_type = cmp_ty_id, 3014 .id_result = overflowed_id, 3015 .operand_1 = rhs_lt_zero_id, 3016 .operand_2 = value_gt_lhs_id, 3017 }); 3018 break :blk overflowed_id; 3019 }, 3020 }; 3021 3022 ov_id.* = try self.intFromBool(wip_ov.ty, overflowed_id); 3023 } 3024 3025 return try self.constructStruct( 3026 result_ty, 3027 &.{ operand_ty, ov_ty }, 3028 &.{ try wip_result.finalize(), try wip_ov.finalize() }, 3029 ); 3030 } 3031 3032 fn airMulOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3033 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 3034 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 3035 const lhs = try self.resolve(extra.lhs); 3036 const rhs = try self.resolve(extra.rhs); 3037 3038 const result_ty = self.typeOfIndex(inst); 3039 const operand_ty = self.typeOf(extra.lhs); 3040 const ov_ty = result_ty.structFieldType(1, self.module); 3041 3042 const info = self.arithmeticTypeInfo(operand_ty); 3043 switch (info.class) { 3044 .composite_integer => return self.todo("overflow ops for composite integers", .{}), 3045 .strange_integer, .integer => {}, 3046 .float, .bool => unreachable, 3047 } 3048 3049 var wip_result = try self.elementWise(operand_ty, true); 3050 defer wip_result.deinit(); 3051 var wip_ov = try self.elementWise(ov_ty, true); 3052 defer wip_ov.deinit(); 3053 3054 const zero_id = try self.constInt(wip_result.ty, 0, .direct); 3055 const zero_ov_id = try self.constInt(wip_ov.ty, 0, .direct); 3056 const one_ov_id = try self.constInt(wip_ov.ty, 1, .direct); 3057 3058 for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { 3059 const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); 3060 const rhs_elem_id = try wip_result.elementAt(operand_ty, rhs, i); 3061 3062 result_id.* = try self.arithOp(wip_result.ty, lhs_elem_id, rhs_elem_id, .OpFMul, .OpIMul, .OpIMul); 3063 3064 // (a != 0) and (x / a != b) 3065 const not_zero_id = try self.cmp(.neq, Type.bool, wip_result.ty, lhs_elem_id, zero_id); 3066 const res_rhs_id = try self.arithOp(wip_result.ty, result_id.*, lhs_elem_id, .OpFDiv, .OpSDiv, .OpUDiv); 3067 const res_rhs_not_rhs_id = try self.cmp(.neq, Type.bool, wip_result.ty, res_rhs_id, rhs_elem_id); 3068 const cond_id = try self.binOpSimple(Type.bool, not_zero_id, res_rhs_not_rhs_id, .OpLogicalAnd); 3069 3070 ov_id.* = self.spv.allocId(); 3071 try self.func.body.emit(self.spv.gpa, .OpSelect, .{ 3072 .id_result_type = wip_ov.ty_id, 3073 .id_result = ov_id.*, 3074 .condition = cond_id, 3075 .object_1 = one_ov_id, 3076 .object_2 = zero_ov_id, 3077 }); 3078 } 3079 3080 return try self.constructStruct( 3081 result_ty, 3082 &.{ operand_ty, ov_ty }, 3083 &.{ try wip_result.finalize(), try wip_ov.finalize() }, 3084 ); 3085 } 3086 3087 fn airShlOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3088 const mod = self.module; 3089 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 3090 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 3091 const lhs = try self.resolve(extra.lhs); 3092 const rhs = try self.resolve(extra.rhs); 3093 3094 const result_ty = self.typeOfIndex(inst); 3095 const operand_ty = self.typeOf(extra.lhs); 3096 const shift_ty = self.typeOf(extra.rhs); 3097 const scalar_shift_ty_id = try self.resolveType(shift_ty.scalarType(mod), .direct); 3098 const scalar_operand_ty_id = try self.resolveType(operand_ty.scalarType(mod), .direct); 3099 3100 const ov_ty = result_ty.structFieldType(1, self.module); 3101 3102 const bool_ty_id = try self.resolveType(Type.bool, .direct); 3103 const cmp_ty_id = if (self.isVector(operand_ty)) 3104 // TODO: Resolving a vector type with .direct should return a SPIR-V vector 3105 try self.spv.vectorType(operand_ty.vectorLen(mod), try self.resolveType(Type.bool, .direct)) 3106 else 3107 bool_ty_id; 3108 3109 const info = self.arithmeticTypeInfo(operand_ty); 3110 switch (info.class) { 3111 .composite_integer => return self.todo("overflow shift for composite integers", .{}), 3112 .integer, .strange_integer => {}, 3113 .float, .bool => unreachable, 3114 } 3115 3116 var wip_result = try self.elementWise(operand_ty, false); 3117 defer wip_result.deinit(); 3118 var wip_ov = try self.elementWise(ov_ty, false); 3119 defer wip_ov.deinit(); 3120 for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { 3121 const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); 3122 const rhs_elem_id = try wip_result.elementAt(shift_ty, rhs, i); 3123 3124 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, 3125 // so just manually upcast it if required. 3126 const shift_id = if (scalar_shift_ty_id != scalar_operand_ty_id) blk: { 3127 const shift_id = self.spv.allocId(); 3128 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ 3129 .id_result_type = wip_result.ty_id, 3130 .id_result = shift_id, 3131 .unsigned_value = rhs_elem_id, 3132 }); 3133 break :blk shift_id; 3134 } else rhs_elem_id; 3135 3136 const value_id = self.spv.allocId(); 3137 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ 3138 .id_result_type = wip_result.ty_id, 3139 .id_result = value_id, 3140 .base = lhs_elem_id, 3141 .shift = shift_id, 3142 }); 3143 result_id.* = try self.normalize(wip_result.ty, value_id, info); 3144 3145 const right_shift_id = self.spv.allocId(); 3146 switch (info.signedness) { 3147 .signed => { 3148 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ 3149 .id_result_type = wip_result.ty_id, 3150 .id_result = right_shift_id, 3151 .base = result_id.*, 3152 .shift = shift_id, 3153 }); 3154 }, 3155 .unsigned => { 3156 try self.func.body.emit(self.spv.gpa, .OpShiftRightLogical, .{ 3157 .id_result_type = wip_result.ty_id, 3158 .id_result = right_shift_id, 3159 .base = result_id.*, 3160 .shift = shift_id, 3161 }); 3162 }, 3163 } 3164 3165 const overflowed_id = self.spv.allocId(); 3166 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ 3167 .id_result_type = cmp_ty_id, 3168 .id_result = overflowed_id, 3169 .operand_1 = lhs_elem_id, 3170 .operand_2 = right_shift_id, 3171 }); 3172 3173 ov_id.* = try self.intFromBool(wip_ov.ty, overflowed_id); 3174 } 3175 3176 return try self.constructStruct( 3177 result_ty, 3178 &.{ operand_ty, ov_ty }, 3179 &.{ try wip_result.finalize(), try wip_ov.finalize() }, 3180 ); 3181 } 3182 3183 fn airMulAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3184 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 3185 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 3186 3187 const mulend1 = try self.resolve(extra.lhs); 3188 const mulend2 = try self.resolve(extra.rhs); 3189 const addend = try self.resolve(pl_op.operand); 3190 3191 const ty = self.typeOfIndex(inst); 3192 3193 const info = self.arithmeticTypeInfo(ty); 3194 assert(info.class == .float); // .mul_add is only emitted for floats 3195 3196 var wip = try self.elementWise(ty, false); 3197 defer wip.deinit(); 3198 for (0..wip.results.len) |i| { 3199 const mul_result = self.spv.allocId(); 3200 try self.func.body.emit(self.spv.gpa, .OpFMul, .{ 3201 .id_result_type = wip.ty_id, 3202 .id_result = mul_result, 3203 .operand_1 = try wip.elementAt(ty, mulend1, i), 3204 .operand_2 = try wip.elementAt(ty, mulend2, i), 3205 }); 3206 3207 try self.func.body.emit(self.spv.gpa, .OpFAdd, .{ 3208 .id_result_type = wip.ty_id, 3209 .id_result = wip.allocId(i), 3210 .operand_1 = mul_result, 3211 .operand_2 = try wip.elementAt(ty, addend, i), 3212 }); 3213 } 3214 return try wip.finalize(); 3215 } 3216 3217 fn airClzCtz(self: *DeclGen, inst: Air.Inst.Index, op: enum { clz, ctz }) !?IdRef { 3218 if (self.liveness.isUnused(inst)) return null; 3219 3220 const mod = self.module; 3221 const target = self.getTarget(); 3222 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 3223 const result_ty = self.typeOfIndex(inst); 3224 const operand_ty = self.typeOf(ty_op.operand); 3225 const operand = try self.resolve(ty_op.operand); 3226 3227 const info = self.arithmeticTypeInfo(operand_ty); 3228 switch (info.class) { 3229 .composite_integer => unreachable, // TODO 3230 .integer, .strange_integer => {}, 3231 .float, .bool => unreachable, 3232 } 3233 3234 var wip = try self.elementWise(result_ty, false); 3235 defer wip.deinit(); 3236 3237 const elem_ty = if (wip.is_array) operand_ty.scalarType(mod) else operand_ty; 3238 const elem_ty_id = try self.resolveType(elem_ty, .direct); 3239 3240 for (wip.results, 0..) |*result_id, i| { 3241 const elem = try wip.elementAt(operand_ty, operand, i); 3242 3243 switch (target.os.tag) { 3244 .opencl => { 3245 const set = try self.spv.importInstructionSet(.@"OpenCL.std"); 3246 const ext_inst: u32 = switch (op) { 3247 .clz => 151, // clz 3248 .ctz => 152, // ctz 3249 }; 3250 3251 // Note: result of OpenCL ctz/clz returns operand_ty, and we want result_ty. 3252 // result_ty is always large enough to hold the result, so we might have to down 3253 // cast it. 3254 const tmp = self.spv.allocId(); 3255 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{ 3256 .id_result_type = elem_ty_id, 3257 .id_result = tmp, 3258 .set = set, 3259 .instruction = .{ .inst = ext_inst }, 3260 .id_ref_4 = &.{elem}, 3261 }); 3262 3263 // TODO: Comparison should be removed.. 3264 // Its valid because SpvModule caches numeric types 3265 if (wip.ty_id == elem_ty_id) { 3266 result_id.* = tmp; 3267 continue; 3268 } 3269 3270 result_id.* = self.spv.allocId(); 3271 if (result_ty.scalarType(mod).isSignedInt(mod)) { 3272 assert(elem_ty.scalarType(mod).isSignedInt(mod)); 3273 try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ 3274 .id_result_type = wip.ty_id, 3275 .id_result = result_id.*, 3276 .signed_value = tmp, 3277 }); 3278 } else { 3279 assert(elem_ty.scalarType(mod).isUnsignedInt(mod)); 3280 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ 3281 .id_result_type = wip.ty_id, 3282 .id_result = result_id.*, 3283 .unsigned_value = tmp, 3284 }); 3285 } 3286 }, 3287 .vulkan => unreachable, // TODO 3288 else => unreachable, 3289 } 3290 } 3291 3292 return try wip.finalize(); 3293 } 3294 3295 fn airSplat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3296 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 3297 const operand_id = try self.resolve(ty_op.operand); 3298 const result_ty = self.typeOfIndex(inst); 3299 var wip = try self.elementWise(result_ty, true); 3300 defer wip.deinit(); 3301 @memset(wip.results, operand_id); 3302 return try wip.finalize(); 3303 } 3304 3305 fn airReduce(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3306 const mod = self.module; 3307 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; 3308 const operand = try self.resolve(reduce.operand); 3309 const operand_ty = self.typeOf(reduce.operand); 3310 const scalar_ty = operand_ty.scalarType(mod); 3311 const scalar_ty_id = try self.resolveType(scalar_ty, .direct); 3312 3313 const info = self.arithmeticTypeInfo(operand_ty); 3314 3315 var result_id = try self.extractField(scalar_ty, operand, 0); 3316 const len = operand_ty.vectorLen(mod); 3317 3318 switch (reduce.operation) { 3319 .Min, .Max => |op| { 3320 const cmp_op: std.math.CompareOperator = if (op == .Max) .gt else .lt; 3321 for (1..len) |i| { 3322 const lhs = result_id; 3323 const rhs = try self.extractField(scalar_ty, operand, @intCast(i)); 3324 result_id = try self.minMax(scalar_ty, cmp_op, lhs, rhs); 3325 } 3326 3327 return result_id; 3328 }, 3329 else => {}, 3330 } 3331 3332 const opcode: Opcode = switch (info.class) { 3333 .bool => switch (reduce.operation) { 3334 .And => .OpLogicalAnd, 3335 .Or => .OpLogicalOr, 3336 .Xor => .OpLogicalNotEqual, 3337 else => unreachable, 3338 }, 3339 .strange_integer, .integer => switch (reduce.operation) { 3340 .And => .OpBitwiseAnd, 3341 .Or => .OpBitwiseOr, 3342 .Xor => .OpBitwiseXor, 3343 .Add => .OpIAdd, 3344 .Mul => .OpIMul, 3345 else => unreachable, 3346 }, 3347 .float => switch (reduce.operation) { 3348 .Add => .OpFAdd, 3349 .Mul => .OpFMul, 3350 else => unreachable, 3351 }, 3352 .composite_integer => unreachable, // TODO 3353 }; 3354 3355 for (1..len) |i| { 3356 const lhs = result_id; 3357 const rhs = try self.extractField(scalar_ty, operand, @intCast(i)); 3358 result_id = self.spv.allocId(); 3359 3360 try self.func.body.emitRaw(self.spv.gpa, opcode, 4); 3361 self.func.body.writeOperand(spec.IdResultType, scalar_ty_id); 3362 self.func.body.writeOperand(spec.IdResult, result_id); 3363 self.func.body.writeOperand(spec.IdResultType, lhs); 3364 self.func.body.writeOperand(spec.IdResultType, rhs); 3365 } 3366 3367 return result_id; 3368 } 3369 3370 fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3371 const mod = self.module; 3372 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 3373 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; 3374 const a = try self.resolve(extra.a); 3375 const b = try self.resolve(extra.b); 3376 const mask = Value.fromInterned(extra.mask); 3377 3378 const ty = self.typeOfIndex(inst); 3379 3380 var wip = try self.elementWise(ty, true); 3381 defer wip.deinit(); 3382 for (wip.results, 0..) |*result_id, i| { 3383 const elem = try mask.elemValue(mod, i); 3384 if (elem.isUndef(mod)) { 3385 result_id.* = try self.spv.constUndef(wip.ty_id); 3386 continue; 3387 } 3388 3389 const index = elem.toSignedInt(mod); 3390 if (index >= 0) { 3391 result_id.* = try self.extractField(wip.ty, a, @intCast(index)); 3392 } else { 3393 result_id.* = try self.extractField(wip.ty, b, @intCast(~index)); 3394 } 3395 } 3396 return try wip.finalize(); 3397 } 3398 3399 fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef { 3400 const ids = try self.gpa.alloc(IdRef, indices.len); 3401 errdefer self.gpa.free(ids); 3402 for (indices, ids) |index, *id| { 3403 id.* = try self.constInt(Type.u32, index, .direct); 3404 } 3405 3406 return ids; 3407 } 3408 3409 fn accessChainId( 3410 self: *DeclGen, 3411 result_ty_id: IdRef, 3412 base: IdRef, 3413 indices: []const IdRef, 3414 ) !IdRef { 3415 const result_id = self.spv.allocId(); 3416 try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ 3417 .id_result_type = result_ty_id, 3418 .id_result = result_id, 3419 .base = base, 3420 .indexes = indices, 3421 }); 3422 return result_id; 3423 } 3424 3425 /// AccessChain is essentially PtrAccessChain with 0 as initial argument. The effective 3426 /// difference lies in whether the resulting type of the first dereference will be the 3427 /// same as that of the base pointer, or that of a dereferenced base pointer. AccessChain 3428 /// is the latter and PtrAccessChain is the former. 3429 fn accessChain( 3430 self: *DeclGen, 3431 result_ty_id: IdRef, 3432 base: IdRef, 3433 indices: []const u32, 3434 ) !IdRef { 3435 const ids = try self.indicesToIds(indices); 3436 defer self.gpa.free(ids); 3437 return try self.accessChainId(result_ty_id, base, ids); 3438 } 3439 3440 fn ptrAccessChain( 3441 self: *DeclGen, 3442 result_ty_id: IdRef, 3443 base: IdRef, 3444 element: IdRef, 3445 indices: []const u32, 3446 ) !IdRef { 3447 const ids = try self.indicesToIds(indices); 3448 defer self.gpa.free(ids); 3449 3450 const result_id = self.spv.allocId(); 3451 try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{ 3452 .id_result_type = result_ty_id, 3453 .id_result = result_id, 3454 .base = base, 3455 .element = element, 3456 .indexes = ids, 3457 }); 3458 return result_id; 3459 } 3460 3461 fn ptrAdd(self: *DeclGen, result_ty: Type, ptr_ty: Type, ptr_id: IdRef, offset_id: IdRef) !IdRef { 3462 const mod = self.module; 3463 const result_ty_id = try self.resolveType(result_ty, .direct); 3464 3465 switch (ptr_ty.ptrSize(mod)) { 3466 .One => { 3467 // Pointer to array 3468 // TODO: Is this correct? 3469 return try self.accessChainId(result_ty_id, ptr_id, &.{offset_id}); 3470 }, 3471 .C, .Many => { 3472 return try self.ptrAccessChain(result_ty_id, ptr_id, offset_id, &.{}); 3473 }, 3474 .Slice => { 3475 // TODO: This is probably incorrect. A slice should be returned here, though this is what llvm does. 3476 const slice_ptr_id = try self.extractField(result_ty, ptr_id, 0); 3477 return try self.ptrAccessChain(result_ty_id, slice_ptr_id, offset_id, &.{}); 3478 }, 3479 } 3480 } 3481 3482 fn airPtrAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3483 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 3484 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 3485 const ptr_id = try self.resolve(bin_op.lhs); 3486 const offset_id = try self.resolve(bin_op.rhs); 3487 const ptr_ty = self.typeOf(bin_op.lhs); 3488 const result_ty = self.typeOfIndex(inst); 3489 3490 return try self.ptrAdd(result_ty, ptr_ty, ptr_id, offset_id); 3491 } 3492 3493 fn airPtrSub(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3494 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 3495 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 3496 const ptr_id = try self.resolve(bin_op.lhs); 3497 const ptr_ty = self.typeOf(bin_op.lhs); 3498 const offset_id = try self.resolve(bin_op.rhs); 3499 const offset_ty = self.typeOf(bin_op.rhs); 3500 const offset_ty_id = try self.resolveType(offset_ty, .direct); 3501 const result_ty = self.typeOfIndex(inst); 3502 3503 const negative_offset_id = self.spv.allocId(); 3504 try self.func.body.emit(self.spv.gpa, .OpSNegate, .{ 3505 .id_result_type = offset_ty_id, 3506 .id_result = negative_offset_id, 3507 .operand = offset_id, 3508 }); 3509 return try self.ptrAdd(result_ty, ptr_ty, ptr_id, negative_offset_id); 3510 } 3511 3512 fn cmp( 3513 self: *DeclGen, 3514 op: std.math.CompareOperator, 3515 result_ty: Type, 3516 ty: Type, 3517 lhs_id: IdRef, 3518 rhs_id: IdRef, 3519 ) !IdRef { 3520 const mod = self.module; 3521 var cmp_lhs_id = lhs_id; 3522 var cmp_rhs_id = rhs_id; 3523 const bool_ty_id = try self.resolveType(Type.bool, .direct); 3524 const op_ty = switch (ty.zigTypeTag(mod)) { 3525 .Int, .Bool, .Float => ty, 3526 .Enum => ty.intTagType(mod), 3527 .ErrorSet => Type.u16, 3528 .Pointer => blk: { 3529 // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are 3530 // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using 3531 // OpConvertPtrToU... 3532 cmp_lhs_id = self.spv.allocId(); 3533 cmp_rhs_id = self.spv.allocId(); 3534 3535 const usize_ty_id = try self.resolveType(Type.usize, .direct); 3536 3537 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ 3538 .id_result_type = usize_ty_id, 3539 .id_result = cmp_lhs_id, 3540 .pointer = lhs_id, 3541 }); 3542 3543 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ 3544 .id_result_type = usize_ty_id, 3545 .id_result = cmp_rhs_id, 3546 .pointer = rhs_id, 3547 }); 3548 3549 break :blk Type.usize; 3550 }, 3551 .Optional => { 3552 const payload_ty = ty.optionalChild(mod); 3553 if (ty.optionalReprIsPayload(mod)) { 3554 assert(payload_ty.hasRuntimeBitsIgnoreComptime(mod)); 3555 assert(!payload_ty.isSlice(mod)); 3556 return self.cmp(op, Type.bool, payload_ty, lhs_id, rhs_id); 3557 } 3558 3559 const lhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) 3560 try self.extractField(Type.bool, lhs_id, 1) 3561 else 3562 try self.convertToDirect(Type.bool, lhs_id); 3563 3564 const rhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) 3565 try self.extractField(Type.bool, rhs_id, 1) 3566 else 3567 try self.convertToDirect(Type.bool, rhs_id); 3568 3569 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { 3570 return try self.cmp(op, Type.bool, Type.bool, lhs_valid_id, rhs_valid_id); 3571 } 3572 3573 // a = lhs_valid 3574 // b = rhs_valid 3575 // c = lhs_pl == rhs_pl 3576 // 3577 // For op == .eq we have: 3578 // a == b && a -> c 3579 // = a == b && (!a || c) 3580 // 3581 // For op == .neq we have 3582 // a == b && a -> c 3583 // = !(a == b && a -> c) 3584 // = a != b || !(a -> c 3585 // = a != b || !(!a || c) 3586 // = a != b || a && !c 3587 3588 const lhs_pl_id = try self.extractField(payload_ty, lhs_id, 0); 3589 const rhs_pl_id = try self.extractField(payload_ty, rhs_id, 0); 3590 3591 switch (op) { 3592 .eq => { 3593 const valid_eq_id = try self.cmp(.eq, Type.bool, Type.bool, lhs_valid_id, rhs_valid_id); 3594 const pl_eq_id = try self.cmp(op, Type.bool, payload_ty, lhs_pl_id, rhs_pl_id); 3595 const lhs_not_valid_id = self.spv.allocId(); 3596 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{ 3597 .id_result_type = bool_ty_id, 3598 .id_result = lhs_not_valid_id, 3599 .operand = lhs_valid_id, 3600 }); 3601 const impl_id = self.spv.allocId(); 3602 try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{ 3603 .id_result_type = bool_ty_id, 3604 .id_result = impl_id, 3605 .operand_1 = lhs_not_valid_id, 3606 .operand_2 = pl_eq_id, 3607 }); 3608 const result_id = self.spv.allocId(); 3609 try self.func.body.emit(self.spv.gpa, .OpLogicalAnd, .{ 3610 .id_result_type = bool_ty_id, 3611 .id_result = result_id, 3612 .operand_1 = valid_eq_id, 3613 .operand_2 = impl_id, 3614 }); 3615 return result_id; 3616 }, 3617 .neq => { 3618 const valid_neq_id = try self.cmp(.neq, Type.bool, Type.bool, lhs_valid_id, rhs_valid_id); 3619 const pl_neq_id = try self.cmp(op, Type.bool, payload_ty, lhs_pl_id, rhs_pl_id); 3620 3621 const impl_id = self.spv.allocId(); 3622 try self.func.body.emit(self.spv.gpa, .OpLogicalAnd, .{ 3623 .id_result_type = bool_ty_id, 3624 .id_result = impl_id, 3625 .operand_1 = lhs_valid_id, 3626 .operand_2 = pl_neq_id, 3627 }); 3628 const result_id = self.spv.allocId(); 3629 try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{ 3630 .id_result_type = bool_ty_id, 3631 .id_result = result_id, 3632 .operand_1 = valid_neq_id, 3633 .operand_2 = impl_id, 3634 }); 3635 return result_id; 3636 }, 3637 else => unreachable, 3638 } 3639 }, 3640 .Vector => { 3641 var wip = try self.elementWise(result_ty, true); 3642 defer wip.deinit(); 3643 const scalar_ty = ty.scalarType(mod); 3644 for (wip.results, 0..) |*result_id, i| { 3645 const lhs_elem_id = try wip.elementAt(ty, lhs_id, i); 3646 const rhs_elem_id = try wip.elementAt(ty, rhs_id, i); 3647 result_id.* = try self.cmp(op, Type.bool, scalar_ty, lhs_elem_id, rhs_elem_id); 3648 } 3649 return wip.finalize(); 3650 }, 3651 else => unreachable, 3652 }; 3653 3654 const opcode: Opcode = opcode: { 3655 const info = self.arithmeticTypeInfo(op_ty); 3656 const signedness = switch (info.class) { 3657 .composite_integer => { 3658 return self.todo("binary operations for composite integers", .{}); 3659 }, 3660 .float => break :opcode switch (op) { 3661 .eq => .OpFOrdEqual, 3662 .neq => .OpFUnordNotEqual, 3663 .lt => .OpFOrdLessThan, 3664 .lte => .OpFOrdLessThanEqual, 3665 .gt => .OpFOrdGreaterThan, 3666 .gte => .OpFOrdGreaterThanEqual, 3667 }, 3668 .bool => break :opcode switch (op) { 3669 .eq => .OpLogicalEqual, 3670 .neq => .OpLogicalNotEqual, 3671 else => unreachable, 3672 }, 3673 .integer, .strange_integer => info.signedness, 3674 }; 3675 3676 break :opcode switch (signedness) { 3677 .unsigned => switch (op) { 3678 .eq => .OpIEqual, 3679 .neq => .OpINotEqual, 3680 .lt => .OpULessThan, 3681 .lte => .OpULessThanEqual, 3682 .gt => .OpUGreaterThan, 3683 .gte => .OpUGreaterThanEqual, 3684 }, 3685 .signed => switch (op) { 3686 .eq => .OpIEqual, 3687 .neq => .OpINotEqual, 3688 .lt => .OpSLessThan, 3689 .lte => .OpSLessThanEqual, 3690 .gt => .OpSGreaterThan, 3691 .gte => .OpSGreaterThanEqual, 3692 }, 3693 }; 3694 }; 3695 3696 const result_id = self.spv.allocId(); 3697 try self.func.body.emitRaw(self.spv.gpa, opcode, 4); 3698 self.func.body.writeOperand(spec.IdResultType, bool_ty_id); 3699 self.func.body.writeOperand(spec.IdResult, result_id); 3700 self.func.body.writeOperand(spec.IdResultType, cmp_lhs_id); 3701 self.func.body.writeOperand(spec.IdResultType, cmp_rhs_id); 3702 return result_id; 3703 } 3704 3705 fn airCmp( 3706 self: *DeclGen, 3707 inst: Air.Inst.Index, 3708 comptime op: std.math.CompareOperator, 3709 ) !?IdRef { 3710 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 3711 const lhs_id = try self.resolve(bin_op.lhs); 3712 const rhs_id = try self.resolve(bin_op.rhs); 3713 const ty = self.typeOf(bin_op.lhs); 3714 const result_ty = self.typeOfIndex(inst); 3715 3716 return try self.cmp(op, result_ty, ty, lhs_id, rhs_id); 3717 } 3718 3719 fn airVectorCmp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3720 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 3721 const vec_cmp = self.air.extraData(Air.VectorCmp, ty_pl.payload).data; 3722 const lhs_id = try self.resolve(vec_cmp.lhs); 3723 const rhs_id = try self.resolve(vec_cmp.rhs); 3724 const op = vec_cmp.compareOperator(); 3725 const ty = self.typeOf(vec_cmp.lhs); 3726 const result_ty = self.typeOfIndex(inst); 3727 3728 return try self.cmp(op, result_ty, ty, lhs_id, rhs_id); 3729 } 3730 3731 /// Bitcast one type to another. Note: both types, input, output are expected in **direct** representation. 3732 fn bitCast( 3733 self: *DeclGen, 3734 dst_ty: Type, 3735 src_ty: Type, 3736 src_id: IdRef, 3737 ) !IdRef { 3738 const mod = self.module; 3739 const src_ty_id = try self.resolveType(src_ty, .direct); 3740 const dst_ty_id = try self.resolveType(dst_ty, .direct); 3741 3742 const result_id = blk: { 3743 if (src_ty_id == dst_ty_id) { 3744 break :blk src_id; 3745 } 3746 3747 // TODO: Some more cases are missing here 3748 // See fn bitCast in llvm.zig 3749 3750 if (src_ty.zigTypeTag(mod) == .Int and dst_ty.isPtrAtRuntime(mod)) { 3751 const result_id = self.spv.allocId(); 3752 try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{ 3753 .id_result_type = dst_ty_id, 3754 .id_result = result_id, 3755 .integer_value = src_id, 3756 }); 3757 break :blk result_id; 3758 } 3759 3760 // We can only use OpBitcast for specific conversions: between numerical types, and 3761 // between pointers. If the resolved spir-v types fall into this category then emit OpBitcast, 3762 // otherwise use a temporary and perform a pointer cast. 3763 const can_bitcast = (src_ty.isNumeric(mod) and dst_ty.isNumeric(mod)) or (src_ty.isPtrAtRuntime(mod) and dst_ty.isPtrAtRuntime(mod)); 3764 if (can_bitcast) { 3765 const result_id = self.spv.allocId(); 3766 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 3767 .id_result_type = dst_ty_id, 3768 .id_result = result_id, 3769 .operand = src_id, 3770 }); 3771 3772 break :blk result_id; 3773 } 3774 3775 const dst_ptr_ty_id = try self.ptrType(dst_ty, .Function); 3776 3777 const tmp_id = try self.alloc(src_ty, .{ .storage_class = .Function }); 3778 try self.store(src_ty, tmp_id, src_id, .{}); 3779 const casted_ptr_id = self.spv.allocId(); 3780 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 3781 .id_result_type = dst_ptr_ty_id, 3782 .id_result = casted_ptr_id, 3783 .operand = tmp_id, 3784 }); 3785 break :blk try self.load(dst_ty, casted_ptr_id, .{}); 3786 }; 3787 3788 // Because strange integers use sign-extended representation, we may need to normalize 3789 // the result here. 3790 // TODO: This detail could cause stuff like @as(*const i1, @ptrCast(&@as(u1, 1))) to break 3791 // should we change the representation of strange integers? 3792 if (dst_ty.zigTypeTag(mod) == .Int) { 3793 const info = self.arithmeticTypeInfo(dst_ty); 3794 return try self.normalize(dst_ty, result_id, info); 3795 } 3796 3797 return result_id; 3798 } 3799 3800 fn airBitCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3801 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 3802 const operand_id = try self.resolve(ty_op.operand); 3803 const operand_ty = self.typeOf(ty_op.operand); 3804 const result_ty = self.typeOfIndex(inst); 3805 return try self.bitCast(result_ty, operand_ty, operand_id); 3806 } 3807 3808 fn airIntCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3809 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 3810 const operand_id = try self.resolve(ty_op.operand); 3811 const src_ty = self.typeOf(ty_op.operand); 3812 const dst_ty = self.typeOfIndex(inst); 3813 3814 const src_info = self.arithmeticTypeInfo(src_ty); 3815 const dst_info = self.arithmeticTypeInfo(dst_ty); 3816 3817 if (src_info.backing_bits == dst_info.backing_bits) { 3818 return operand_id; 3819 } 3820 3821 var wip = try self.elementWise(dst_ty, false); 3822 defer wip.deinit(); 3823 for (wip.results, 0..) |*result_id, i| { 3824 const elem_id = try wip.elementAt(src_ty, operand_id, i); 3825 const value_id = self.spv.allocId(); 3826 switch (dst_info.signedness) { 3827 .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ 3828 .id_result_type = wip.ty_id, 3829 .id_result = value_id, 3830 .signed_value = elem_id, 3831 }), 3832 .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ 3833 .id_result_type = wip.ty_id, 3834 .id_result = value_id, 3835 .unsigned_value = elem_id, 3836 }), 3837 } 3838 3839 // Make sure to normalize the result if shrinking. 3840 // Because strange ints are sign extended in their backing 3841 // type, we don't need to normalize when growing the type. The 3842 // representation is already the same. 3843 if (dst_info.bits < src_info.bits) { 3844 result_id.* = try self.normalize(wip.ty, value_id, dst_info); 3845 } else { 3846 result_id.* = value_id; 3847 } 3848 } 3849 return try wip.finalize(); 3850 } 3851 3852 fn intFromPtr(self: *DeclGen, operand_id: IdRef) !IdRef { 3853 const result_type_id = try self.resolveType(Type.usize, .direct); 3854 const result_id = self.spv.allocId(); 3855 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ 3856 .id_result_type = result_type_id, 3857 .id_result = result_id, 3858 .pointer = operand_id, 3859 }); 3860 return result_id; 3861 } 3862 3863 fn airIntFromPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3864 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 3865 const operand_id = try self.resolve(un_op); 3866 return try self.intFromPtr(operand_id); 3867 } 3868 3869 fn airFloatFromInt(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3870 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 3871 const operand_ty = self.typeOf(ty_op.operand); 3872 const operand_id = try self.resolve(ty_op.operand); 3873 const result_ty = self.typeOfIndex(inst); 3874 return try self.floatFromInt(result_ty, operand_ty, operand_id); 3875 } 3876 3877 fn floatFromInt(self: *DeclGen, result_ty: Type, operand_ty: Type, operand_id: IdRef) !IdRef { 3878 const operand_info = self.arithmeticTypeInfo(operand_ty); 3879 const result_id = self.spv.allocId(); 3880 const result_ty_id = try self.resolveType(result_ty, .direct); 3881 switch (operand_info.signedness) { 3882 .signed => try self.func.body.emit(self.spv.gpa, .OpConvertSToF, .{ 3883 .id_result_type = result_ty_id, 3884 .id_result = result_id, 3885 .signed_value = operand_id, 3886 }), 3887 .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertUToF, .{ 3888 .id_result_type = result_ty_id, 3889 .id_result = result_id, 3890 .unsigned_value = operand_id, 3891 }), 3892 } 3893 return result_id; 3894 } 3895 3896 fn airIntFromFloat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3897 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 3898 const operand_id = try self.resolve(ty_op.operand); 3899 const result_ty = self.typeOfIndex(inst); 3900 return try self.intFromFloat(result_ty, operand_id); 3901 } 3902 3903 fn intFromFloat(self: *DeclGen, result_ty: Type, operand_id: IdRef) !IdRef { 3904 const result_info = self.arithmeticTypeInfo(result_ty); 3905 const result_ty_id = try self.resolveType(result_ty, .direct); 3906 const result_id = self.spv.allocId(); 3907 switch (result_info.signedness) { 3908 .signed => try self.func.body.emit(self.spv.gpa, .OpConvertFToS, .{ 3909 .id_result_type = result_ty_id, 3910 .id_result = result_id, 3911 .float_value = operand_id, 3912 }), 3913 .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertFToU, .{ 3914 .id_result_type = result_ty_id, 3915 .id_result = result_id, 3916 .float_value = operand_id, 3917 }), 3918 } 3919 return result_id; 3920 } 3921 3922 fn airIntFromBool(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3923 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 3924 const operand_id = try self.resolve(un_op); 3925 const result_ty = self.typeOfIndex(inst); 3926 3927 var wip = try self.elementWise(result_ty, false); 3928 defer wip.deinit(); 3929 for (wip.results, 0..) |*result_id, i| { 3930 const elem_id = try wip.elementAt(Type.bool, operand_id, i); 3931 result_id.* = try self.intFromBool(wip.ty, elem_id); 3932 } 3933 return try wip.finalize(); 3934 } 3935 3936 fn airFloatCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3937 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 3938 const operand_id = try self.resolve(ty_op.operand); 3939 const dest_ty = self.typeOfIndex(inst); 3940 const dest_ty_id = try self.resolveType(dest_ty, .direct); 3941 3942 const result_id = self.spv.allocId(); 3943 try self.func.body.emit(self.spv.gpa, .OpFConvert, .{ 3944 .id_result_type = dest_ty_id, 3945 .id_result = result_id, 3946 .float_value = operand_id, 3947 }); 3948 return result_id; 3949 } 3950 3951 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3952 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 3953 const operand_id = try self.resolve(ty_op.operand); 3954 const result_ty = self.typeOfIndex(inst); 3955 const info = self.arithmeticTypeInfo(result_ty); 3956 3957 var wip = try self.elementWise(result_ty, false); 3958 defer wip.deinit(); 3959 3960 for (0..wip.results.len) |i| { 3961 const args = .{ 3962 .id_result_type = wip.ty_id, 3963 .id_result = wip.allocId(i), 3964 .operand = try wip.elementAt(result_ty, operand_id, i), 3965 }; 3966 switch (info.class) { 3967 .bool => { 3968 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, args); 3969 }, 3970 .float => unreachable, 3971 .composite_integer => unreachable, // TODO 3972 .strange_integer, .integer => { 3973 // Note: strange integer bits will be masked before operations that do not hold under modulo. 3974 try self.func.body.emit(self.spv.gpa, .OpNot, args); 3975 }, 3976 } 3977 } 3978 3979 return try wip.finalize(); 3980 } 3981 3982 fn airArrayToSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3983 const mod = self.module; 3984 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 3985 const array_ptr_ty = self.typeOf(ty_op.operand); 3986 const array_ty = array_ptr_ty.childType(mod); 3987 const slice_ty = self.typeOfIndex(inst); 3988 const elem_ptr_ty = slice_ty.slicePtrFieldType(mod); 3989 3990 const elem_ptr_ty_id = try self.resolveType(elem_ptr_ty, .direct); 3991 3992 const array_ptr_id = try self.resolve(ty_op.operand); 3993 const len_id = try self.constInt(Type.usize, array_ty.arrayLen(mod), .direct); 3994 3995 const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(mod)) 3996 // Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type. 3997 try self.bitCast(elem_ptr_ty, array_ptr_ty, array_ptr_id) 3998 else 3999 // Convert the pointer-to-array to a pointer to the first element. 4000 try self.accessChain(elem_ptr_ty_id, array_ptr_id, &.{0}); 4001 4002 return try self.constructStruct( 4003 slice_ty, 4004 &.{ elem_ptr_ty, Type.usize }, 4005 &.{ elem_ptr_id, len_id }, 4006 ); 4007 } 4008 4009 fn airSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4010 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 4011 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 4012 const ptr_id = try self.resolve(bin_op.lhs); 4013 const len_id = try self.resolve(bin_op.rhs); 4014 const ptr_ty = self.typeOf(bin_op.lhs); 4015 const slice_ty = self.typeOfIndex(inst); 4016 4017 // Note: Types should not need to be converted to direct, these types 4018 // dont need to be converted. 4019 return try self.constructStruct( 4020 slice_ty, 4021 &.{ ptr_ty, Type.usize }, 4022 &.{ ptr_id, len_id }, 4023 ); 4024 } 4025 4026 fn airAggregateInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4027 const mod = self.module; 4028 const ip = &mod.intern_pool; 4029 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 4030 const result_ty = self.typeOfIndex(inst); 4031 const len: usize = @intCast(result_ty.arrayLen(mod)); 4032 const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]); 4033 4034 switch (result_ty.zigTypeTag(mod)) { 4035 .Struct => { 4036 if (mod.typeToPackedStruct(result_ty)) |struct_type| { 4037 _ = struct_type; 4038 unreachable; // TODO 4039 } 4040 4041 const types = try self.gpa.alloc(Type, elements.len); 4042 defer self.gpa.free(types); 4043 const constituents = try self.gpa.alloc(IdRef, elements.len); 4044 defer self.gpa.free(constituents); 4045 var index: usize = 0; 4046 4047 switch (ip.indexToKey(result_ty.toIntern())) { 4048 .anon_struct_type => |tuple| { 4049 for (tuple.types.get(ip), elements, 0..) |field_ty, element, i| { 4050 if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; 4051 assert(Type.fromInterned(field_ty).hasRuntimeBits(mod)); 4052 4053 const id = try self.resolve(element); 4054 types[index] = Type.fromInterned(field_ty); 4055 constituents[index] = try self.convertToIndirect(Type.fromInterned(field_ty), id); 4056 index += 1; 4057 } 4058 }, 4059 .struct_type => { 4060 const struct_type = ip.loadStructType(result_ty.toIntern()); 4061 var it = struct_type.iterateRuntimeOrder(ip); 4062 for (elements, 0..) |element, i| { 4063 const field_index = it.next().?; 4064 if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; 4065 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); 4066 assert(field_ty.hasRuntimeBitsIgnoreComptime(mod)); 4067 4068 const id = try self.resolve(element); 4069 types[index] = field_ty; 4070 constituents[index] = try self.convertToIndirect(field_ty, id); 4071 index += 1; 4072 } 4073 }, 4074 else => unreachable, 4075 } 4076 4077 return try self.constructStruct( 4078 result_ty, 4079 types[0..index], 4080 constituents[0..index], 4081 ); 4082 }, 4083 .Vector => { 4084 const n_elems = result_ty.vectorLen(mod); 4085 const elem_ids = try self.gpa.alloc(IdRef, n_elems); 4086 defer self.gpa.free(elem_ids); 4087 4088 for (elements, 0..) |element, i| { 4089 const id = try self.resolve(element); 4090 elem_ids[i] = try self.convertToIndirect(result_ty.childType(mod), id); 4091 } 4092 4093 return try self.constructVector(result_ty, elem_ids); 4094 }, 4095 .Array => { 4096 const array_info = result_ty.arrayInfo(mod); 4097 const n_elems: usize = @intCast(result_ty.arrayLenIncludingSentinel(mod)); 4098 const elem_ids = try self.gpa.alloc(IdRef, n_elems); 4099 defer self.gpa.free(elem_ids); 4100 4101 for (elements, 0..) |element, i| { 4102 const id = try self.resolve(element); 4103 elem_ids[i] = try self.convertToIndirect(array_info.elem_type, id); 4104 } 4105 4106 if (array_info.sentinel) |sentinel_val| { 4107 elem_ids[n_elems - 1] = try self.constant(array_info.elem_type, sentinel_val, .indirect); 4108 } 4109 4110 return try self.constructArray(result_ty, elem_ids); 4111 }, 4112 else => unreachable, 4113 } 4114 } 4115 4116 fn sliceOrArrayLen(self: *DeclGen, operand_id: IdRef, ty: Type) !IdRef { 4117 const mod = self.module; 4118 switch (ty.ptrSize(mod)) { 4119 .Slice => return self.extractField(Type.usize, operand_id, 1), 4120 .One => { 4121 const array_ty = ty.childType(mod); 4122 const elem_ty = array_ty.childType(mod); 4123 const abi_size = elem_ty.abiSize(mod); 4124 const size = array_ty.arrayLenIncludingSentinel(mod) * abi_size; 4125 return try self.constInt(Type.usize, size, .direct); 4126 }, 4127 .Many, .C => unreachable, 4128 } 4129 } 4130 4131 fn sliceOrArrayPtr(self: *DeclGen, operand_id: IdRef, ty: Type) !IdRef { 4132 const mod = self.module; 4133 if (ty.isSlice(mod)) { 4134 const ptr_ty = ty.slicePtrFieldType(mod); 4135 return self.extractField(ptr_ty, operand_id, 0); 4136 } 4137 return operand_id; 4138 } 4139 4140 fn airMemcpy(self: *DeclGen, inst: Air.Inst.Index) !void { 4141 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 4142 const dest_slice = try self.resolve(bin_op.lhs); 4143 const src_slice = try self.resolve(bin_op.rhs); 4144 const dest_ty = self.typeOf(bin_op.lhs); 4145 const src_ty = self.typeOf(bin_op.rhs); 4146 const dest_ptr = try self.sliceOrArrayPtr(dest_slice, dest_ty); 4147 const src_ptr = try self.sliceOrArrayPtr(src_slice, src_ty); 4148 const len = try self.sliceOrArrayLen(dest_slice, dest_ty); 4149 try self.func.body.emit(self.spv.gpa, .OpCopyMemorySized, .{ 4150 .target = dest_ptr, 4151 .source = src_ptr, 4152 .size = len, 4153 }); 4154 } 4155 4156 fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef { 4157 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 4158 const field_ty = self.typeOfIndex(inst); 4159 const operand_id = try self.resolve(ty_op.operand); 4160 return try self.extractField(field_ty, operand_id, field); 4161 } 4162 4163 fn airSliceElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4164 const mod = self.module; 4165 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 4166 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 4167 const slice_ty = self.typeOf(bin_op.lhs); 4168 if (!slice_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null; 4169 4170 const slice_id = try self.resolve(bin_op.lhs); 4171 const index_id = try self.resolve(bin_op.rhs); 4172 4173 const ptr_ty = self.typeOfIndex(inst); 4174 const ptr_ty_id = try self.resolveType(ptr_ty, .direct); 4175 4176 const slice_ptr = try self.extractField(ptr_ty, slice_id, 0); 4177 return try self.ptrAccessChain(ptr_ty_id, slice_ptr, index_id, &.{}); 4178 } 4179 4180 fn airSliceElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4181 const mod = self.module; 4182 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 4183 const slice_ty = self.typeOf(bin_op.lhs); 4184 if (!slice_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null; 4185 4186 const slice_id = try self.resolve(bin_op.lhs); 4187 const index_id = try self.resolve(bin_op.rhs); 4188 4189 const ptr_ty = slice_ty.slicePtrFieldType(mod); 4190 const ptr_ty_id = try self.resolveType(ptr_ty, .direct); 4191 4192 const slice_ptr = try self.extractField(ptr_ty, slice_id, 0); 4193 const elem_ptr = try self.ptrAccessChain(ptr_ty_id, slice_ptr, index_id, &.{}); 4194 return try self.load(slice_ty.childType(mod), elem_ptr, .{ .is_volatile = slice_ty.isVolatilePtr(mod) }); 4195 } 4196 4197 fn ptrElemPtr(self: *DeclGen, ptr_ty: Type, ptr_id: IdRef, index_id: IdRef) !IdRef { 4198 const mod = self.module; 4199 // Construct new pointer type for the resulting pointer 4200 const elem_ty = ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T. 4201 const elem_ptr_ty_id = try self.ptrType(elem_ty, self.spvStorageClass(ptr_ty.ptrAddressSpace(mod))); 4202 if (ptr_ty.isSinglePointer(mod)) { 4203 // Pointer-to-array. In this case, the resulting pointer is not of the same type 4204 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain. 4205 return try self.accessChainId(elem_ptr_ty_id, ptr_id, &.{index_id}); 4206 } else { 4207 // Resulting pointer type is the same as the ptr_ty, so use ptrAccessChain 4208 return try self.ptrAccessChain(elem_ptr_ty_id, ptr_id, index_id, &.{}); 4209 } 4210 } 4211 4212 fn airPtrElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4213 const mod = self.module; 4214 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 4215 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 4216 const src_ptr_ty = self.typeOf(bin_op.lhs); 4217 const elem_ty = src_ptr_ty.childType(mod); 4218 const ptr_id = try self.resolve(bin_op.lhs); 4219 4220 if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) { 4221 const dst_ptr_ty = self.typeOfIndex(inst); 4222 return try self.bitCast(dst_ptr_ty, src_ptr_ty, ptr_id); 4223 } 4224 4225 const index_id = try self.resolve(bin_op.rhs); 4226 return try self.ptrElemPtr(src_ptr_ty, ptr_id, index_id); 4227 } 4228 4229 fn airArrayElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4230 const mod = self.module; 4231 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 4232 const array_ty = self.typeOf(bin_op.lhs); 4233 const elem_ty = array_ty.childType(mod); 4234 const array_id = try self.resolve(bin_op.lhs); 4235 const index_id = try self.resolve(bin_op.rhs); 4236 4237 // SPIR-V doesn't have an array indexing function for some damn reason. 4238 // For now, just generate a temporary and use that. 4239 // TODO: This backend probably also should use isByRef from llvm... 4240 4241 const elem_ptr_ty_id = try self.ptrType(elem_ty, .Function); 4242 4243 const tmp_id = try self.alloc(array_ty, .{ .storage_class = .Function }); 4244 try self.store(array_ty, tmp_id, array_id, .{}); 4245 const elem_ptr_id = try self.accessChainId(elem_ptr_ty_id, tmp_id, &.{index_id}); 4246 return try self.load(elem_ty, elem_ptr_id, .{}); 4247 } 4248 4249 fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4250 const mod = self.module; 4251 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 4252 const ptr_ty = self.typeOf(bin_op.lhs); 4253 const elem_ty = self.typeOfIndex(inst); 4254 const ptr_id = try self.resolve(bin_op.lhs); 4255 const index_id = try self.resolve(bin_op.rhs); 4256 const elem_ptr_id = try self.ptrElemPtr(ptr_ty, ptr_id, index_id); 4257 return try self.load(elem_ty, elem_ptr_id, .{ .is_volatile = ptr_ty.isVolatilePtr(mod) }); 4258 } 4259 4260 fn airVectorStoreElem(self: *DeclGen, inst: Air.Inst.Index) !void { 4261 const mod = self.module; 4262 const data = self.air.instructions.items(.data)[@intFromEnum(inst)].vector_store_elem; 4263 const extra = self.air.extraData(Air.Bin, data.payload).data; 4264 4265 const vector_ptr_ty = self.typeOf(data.vector_ptr); 4266 const vector_ty = vector_ptr_ty.childType(mod); 4267 const scalar_ty = vector_ty.scalarType(mod); 4268 4269 const storage_class = self.spvStorageClass(vector_ptr_ty.ptrAddressSpace(mod)); 4270 const scalar_ptr_ty_id = try self.ptrType(scalar_ty, storage_class); 4271 4272 const vector_ptr = try self.resolve(data.vector_ptr); 4273 const index = try self.resolve(extra.lhs); 4274 const operand = try self.resolve(extra.rhs); 4275 4276 const elem_ptr_id = try self.accessChainId(scalar_ptr_ty_id, vector_ptr, &.{index}); 4277 try self.store(scalar_ty, elem_ptr_id, operand, .{ 4278 .is_volatile = vector_ptr_ty.isVolatilePtr(mod), 4279 }); 4280 } 4281 4282 fn airSetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !void { 4283 const mod = self.module; 4284 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 4285 const un_ptr_ty = self.typeOf(bin_op.lhs); 4286 const un_ty = un_ptr_ty.childType(mod); 4287 const layout = self.unionLayout(un_ty); 4288 4289 if (layout.tag_size == 0) return; 4290 4291 const tag_ty = un_ty.unionTagTypeSafety(mod).?; 4292 const tag_ptr_ty_id = try self.ptrType(tag_ty, self.spvStorageClass(un_ptr_ty.ptrAddressSpace(mod))); 4293 4294 const union_ptr_id = try self.resolve(bin_op.lhs); 4295 const new_tag_id = try self.resolve(bin_op.rhs); 4296 4297 if (!layout.has_payload) { 4298 try self.store(tag_ty, union_ptr_id, new_tag_id, .{ .is_volatile = un_ptr_ty.isVolatilePtr(mod) }); 4299 } else { 4300 const ptr_id = try self.accessChain(tag_ptr_ty_id, union_ptr_id, &.{layout.tag_index}); 4301 try self.store(tag_ty, ptr_id, new_tag_id, .{ .is_volatile = un_ptr_ty.isVolatilePtr(mod) }); 4302 } 4303 } 4304 4305 fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4306 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 4307 const un_ty = self.typeOf(ty_op.operand); 4308 4309 const mod = self.module; 4310 const layout = self.unionLayout(un_ty); 4311 if (layout.tag_size == 0) return null; 4312 4313 const union_handle = try self.resolve(ty_op.operand); 4314 if (!layout.has_payload) return union_handle; 4315 4316 const tag_ty = un_ty.unionTagTypeSafety(mod).?; 4317 return try self.extractField(tag_ty, union_handle, layout.tag_index); 4318 } 4319 4320 fn unionInit( 4321 self: *DeclGen, 4322 ty: Type, 4323 active_field: u32, 4324 payload: ?IdRef, 4325 ) !IdRef { 4326 // To initialize a union, generate a temporary variable with the 4327 // union type, then get the field pointer and pointer-cast it to the 4328 // right type to store it. Finally load the entire union. 4329 4330 // Note: The result here is not cached, because it generates runtime code. 4331 4332 const mod = self.module; 4333 const ip = &mod.intern_pool; 4334 const union_ty = mod.typeToUnion(ty).?; 4335 const tag_ty = Type.fromInterned(union_ty.enum_tag_ty); 4336 4337 if (union_ty.getLayout(ip) == .@"packed") { 4338 unreachable; // TODO 4339 } 4340 4341 const layout = self.unionLayout(ty); 4342 4343 const tag_int = if (layout.tag_size != 0) blk: { 4344 const tag_val = try mod.enumValueFieldIndex(tag_ty, active_field); 4345 const tag_int_val = try tag_val.intFromEnum(tag_ty, mod); 4346 break :blk tag_int_val.toUnsignedInt(mod); 4347 } else 0; 4348 4349 if (!layout.has_payload) { 4350 return try self.constInt(tag_ty, tag_int, .direct); 4351 } 4352 4353 const tmp_id = try self.alloc(ty, .{ .storage_class = .Function }); 4354 4355 if (layout.tag_size != 0) { 4356 const tag_ptr_ty_id = try self.ptrType(tag_ty, .Function); 4357 const ptr_id = try self.accessChain(tag_ptr_ty_id, tmp_id, &.{@as(u32, @intCast(layout.tag_index))}); 4358 const tag_id = try self.constInt(tag_ty, tag_int, .direct); 4359 try self.store(tag_ty, ptr_id, tag_id, .{}); 4360 } 4361 4362 const payload_ty = Type.fromInterned(union_ty.field_types.get(ip)[active_field]); 4363 if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { 4364 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function); 4365 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index}); 4366 const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function); 4367 const active_pl_ptr_id = self.spv.allocId(); 4368 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 4369 .id_result_type = active_pl_ptr_ty_id, 4370 .id_result = active_pl_ptr_id, 4371 .operand = pl_ptr_id, 4372 }); 4373 4374 try self.store(payload_ty, active_pl_ptr_id, payload.?, .{}); 4375 } else { 4376 assert(payload == null); 4377 } 4378 4379 // Just leave the padding fields uninitialized... 4380 // TODO: Or should we initialize them with undef explicitly? 4381 4382 return try self.load(ty, tmp_id, .{}); 4383 } 4384 4385 fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4386 const mod = self.module; 4387 const ip = &mod.intern_pool; 4388 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 4389 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; 4390 const ty = self.typeOfIndex(inst); 4391 4392 const union_obj = mod.typeToUnion(ty).?; 4393 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); 4394 const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(mod)) 4395 try self.resolve(extra.init) 4396 else 4397 null; 4398 return try self.unionInit(ty, extra.field_index, payload); 4399 } 4400 4401 fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4402 const mod = self.module; 4403 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 4404 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; 4405 4406 const object_ty = self.typeOf(struct_field.struct_operand); 4407 const object_id = try self.resolve(struct_field.struct_operand); 4408 const field_index = struct_field.field_index; 4409 const field_ty = object_ty.structFieldType(field_index, mod); 4410 4411 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) return null; 4412 4413 switch (object_ty.zigTypeTag(mod)) { 4414 .Struct => switch (object_ty.containerLayout(mod)) { 4415 .@"packed" => unreachable, // TODO 4416 else => return try self.extractField(field_ty, object_id, field_index), 4417 }, 4418 .Union => switch (object_ty.containerLayout(mod)) { 4419 .@"packed" => unreachable, // TODO 4420 else => { 4421 // Store, ptr-elem-ptr, pointer-cast, load 4422 const layout = self.unionLayout(object_ty); 4423 assert(layout.has_payload); 4424 4425 const tmp_id = try self.alloc(object_ty, .{ .storage_class = .Function }); 4426 try self.store(object_ty, tmp_id, object_id, .{}); 4427 4428 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function); 4429 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index}); 4430 4431 const active_pl_ptr_ty_id = try self.ptrType(field_ty, .Function); 4432 const active_pl_ptr_id = self.spv.allocId(); 4433 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 4434 .id_result_type = active_pl_ptr_ty_id, 4435 .id_result = active_pl_ptr_id, 4436 .operand = pl_ptr_id, 4437 }); 4438 return try self.load(field_ty, active_pl_ptr_id, .{}); 4439 }, 4440 }, 4441 else => unreachable, 4442 } 4443 } 4444 4445 fn airFieldParentPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4446 const mod = self.module; 4447 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 4448 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; 4449 4450 const parent_ty = ty_pl.ty.toType().childType(mod); 4451 const result_ty_id = try self.resolveType(ty_pl.ty.toType(), .indirect); 4452 4453 const field_ptr = try self.resolve(extra.field_ptr); 4454 const field_ptr_int = try self.intFromPtr(field_ptr); 4455 const field_offset = parent_ty.structFieldOffset(extra.field_index, mod); 4456 4457 const base_ptr_int = base_ptr_int: { 4458 if (field_offset == 0) break :base_ptr_int field_ptr_int; 4459 4460 const field_offset_id = try self.constInt(Type.usize, field_offset, .direct); 4461 break :base_ptr_int try self.binOpSimple(Type.usize, field_ptr_int, field_offset_id, .OpISub); 4462 }; 4463 4464 const base_ptr = self.spv.allocId(); 4465 try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{ 4466 .id_result_type = result_ty_id, 4467 .id_result = base_ptr, 4468 .integer_value = base_ptr_int, 4469 }); 4470 4471 return base_ptr; 4472 } 4473 4474 fn structFieldPtr( 4475 self: *DeclGen, 4476 result_ptr_ty: Type, 4477 object_ptr_ty: Type, 4478 object_ptr: IdRef, 4479 field_index: u32, 4480 ) !IdRef { 4481 const result_ty_id = try self.resolveType(result_ptr_ty, .direct); 4482 4483 const zcu = self.module; 4484 const object_ty = object_ptr_ty.childType(zcu); 4485 switch (object_ty.zigTypeTag(zcu)) { 4486 .Pointer => { 4487 assert(object_ty.isSlice(zcu)); 4488 return self.accessChain(result_ty_id, object_ptr, &.{field_index}); 4489 }, 4490 .Struct => switch (object_ty.containerLayout(zcu)) { 4491 .@"packed" => unreachable, // TODO 4492 else => { 4493 return try self.accessChain(result_ty_id, object_ptr, &.{field_index}); 4494 }, 4495 }, 4496 .Union => switch (object_ty.containerLayout(zcu)) { 4497 .@"packed" => unreachable, // TODO 4498 else => { 4499 const layout = self.unionLayout(object_ty); 4500 if (!layout.has_payload) { 4501 // Asked to get a pointer to a zero-sized field. Just lower this 4502 // to undefined, there is no reason to make it be a valid pointer. 4503 return try self.spv.constUndef(result_ty_id); 4504 } 4505 4506 const storage_class = self.spvStorageClass(object_ptr_ty.ptrAddressSpace(zcu)); 4507 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, storage_class); 4508 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index}); 4509 4510 const active_pl_ptr_id = self.spv.allocId(); 4511 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 4512 .id_result_type = result_ty_id, 4513 .id_result = active_pl_ptr_id, 4514 .operand = pl_ptr_id, 4515 }); 4516 return active_pl_ptr_id; 4517 }, 4518 }, 4519 else => unreachable, 4520 } 4521 } 4522 4523 fn airStructFieldPtrIndex(self: *DeclGen, inst: Air.Inst.Index, field_index: u32) !?IdRef { 4524 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 4525 const struct_ptr = try self.resolve(ty_op.operand); 4526 const struct_ptr_ty = self.typeOf(ty_op.operand); 4527 const result_ptr_ty = self.typeOfIndex(inst); 4528 return try self.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index); 4529 } 4530 4531 const AllocOptions = struct { 4532 initializer: ?IdRef = null, 4533 /// The final storage class of the pointer. This may be either `.Generic` or `.Function`. 4534 /// In either case, the local is allocated in the `.Function` storage class, and optionally 4535 /// cast back to `.Generic`. 4536 storage_class: StorageClass = .Generic, 4537 }; 4538 4539 // Allocate a function-local variable, with possible initializer. 4540 // This function returns a pointer to a variable of type `ty`, 4541 // which is in the Generic address space. The variable is actually 4542 // placed in the Function address space. 4543 fn alloc( 4544 self: *DeclGen, 4545 ty: Type, 4546 options: AllocOptions, 4547 ) !IdRef { 4548 const ptr_fn_ty_id = try self.ptrType(ty, .Function); 4549 4550 // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to 4551 // directly generate them into func.prologue instead of the body. 4552 const var_id = self.spv.allocId(); 4553 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ 4554 .id_result_type = ptr_fn_ty_id, 4555 .id_result = var_id, 4556 .storage_class = .Function, 4557 .initializer = options.initializer, 4558 }); 4559 4560 const target = self.getTarget(); 4561 if (target.os.tag == .vulkan) { 4562 return var_id; 4563 } 4564 4565 switch (options.storage_class) { 4566 .Generic => { 4567 const ptr_gn_ty_id = try self.ptrType(ty, .Generic); 4568 // Convert to a generic pointer 4569 return self.castToGeneric(ptr_gn_ty_id, var_id); 4570 }, 4571 .Function => return var_id, 4572 else => unreachable, 4573 } 4574 } 4575 4576 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4577 const mod = self.module; 4578 const ptr_ty = self.typeOfIndex(inst); 4579 assert(ptr_ty.ptrAddressSpace(mod) == .generic); 4580 const child_ty = ptr_ty.childType(mod); 4581 return try self.alloc(child_ty, .{}); 4582 } 4583 4584 fn airArg(self: *DeclGen) IdRef { 4585 defer self.next_arg_index += 1; 4586 return self.args.items[self.next_arg_index]; 4587 } 4588 4589 /// Given a slice of incoming block connections, returns the block-id of the next 4590 /// block to jump to. This function emits instructions, so it should be emitted 4591 /// inside the merge block of the block. 4592 /// This function should only be called with structured control flow generation. 4593 fn structuredNextBlock(self: *DeclGen, incoming: []const ControlFlow.Structured.Block.Incoming) !IdRef { 4594 assert(self.control_flow == .structured); 4595 4596 const result_id = self.spv.allocId(); 4597 const block_id_ty_id = try self.resolveType(Type.u32, .direct); 4598 try self.func.body.emitRaw(self.spv.gpa, .OpPhi, @intCast(2 + incoming.len * 2)); // result type + result + variable/parent... 4599 self.func.body.writeOperand(spec.IdResultType, block_id_ty_id); 4600 self.func.body.writeOperand(spec.IdRef, result_id); 4601 4602 for (incoming) |incoming_block| { 4603 self.func.body.writeOperand(spec.PairIdRefIdRef, .{ incoming_block.next_block, incoming_block.src_label }); 4604 } 4605 4606 return result_id; 4607 } 4608 4609 /// Jumps to the block with the target block-id. This function must only be called when 4610 /// terminating a body, there should be no instructions after it. 4611 /// This function should only be called with structured control flow generation. 4612 fn structuredBreak(self: *DeclGen, target_block: IdRef) !void { 4613 assert(self.control_flow == .structured); 4614 4615 const sblock = self.control_flow.structured.block_stack.getLast(); 4616 const merge_block = switch (sblock.*) { 4617 .selection => |*merge| blk: { 4618 const merge_label = self.spv.allocId(); 4619 try merge.merge_stack.append(self.gpa, .{ 4620 .incoming = .{ 4621 .src_label = self.current_block_label, 4622 .next_block = target_block, 4623 }, 4624 .merge_block = merge_label, 4625 }); 4626 break :blk merge_label; 4627 }, 4628 // Loop blocks do not end in a break. Not through a direct break, 4629 // and also not through another instruction like cond_br or unreachable (these 4630 // situations are replaced by `cond_br` in sema, or there is a `block` instruction 4631 // placed around them). 4632 .loop => unreachable, 4633 }; 4634 4635 try self.func.body.emitBranch(self.spv.gpa, merge_block); 4636 } 4637 4638 /// Generate a body in a way that exits the body using only structured constructs. 4639 /// Returns the block-id of the next block to jump to. After this function, a jump 4640 /// should still be emitted to the block that should follow this structured body. 4641 /// This function should only be called with structured control flow generation. 4642 fn genStructuredBody( 4643 self: *DeclGen, 4644 /// This parameter defines the method that this structured body is exited with. 4645 block_merge_type: union(enum) { 4646 /// Using selection; early exits from this body are surrounded with 4647 /// if() statements. 4648 selection, 4649 /// Using loops; loops can be early exited by jumping to the merge block at 4650 /// any time. 4651 loop: struct { 4652 merge_label: IdRef, 4653 continue_label: IdRef, 4654 }, 4655 }, 4656 body: []const Air.Inst.Index, 4657 ) !IdRef { 4658 assert(self.control_flow == .structured); 4659 4660 var sblock: ControlFlow.Structured.Block = switch (block_merge_type) { 4661 .loop => |merge| .{ .loop = .{ 4662 .merge_block = merge.merge_label, 4663 } }, 4664 .selection => .{ .selection = .{} }, 4665 }; 4666 defer sblock.deinit(self.gpa); 4667 4668 { 4669 try self.control_flow.structured.block_stack.append(self.gpa, &sblock); 4670 defer _ = self.control_flow.structured.block_stack.pop(); 4671 4672 try self.genBody(body); 4673 } 4674 4675 switch (sblock) { 4676 .selection => |merge| { 4677 // Now generate the merge block for all merges that 4678 // still need to be performed. 4679 const merge_stack = merge.merge_stack.items; 4680 4681 // If no merges on the stack, this block didn't generate any jumps (all paths 4682 // ended with a return or an unreachable). In that case, we don't need to do 4683 // any merging. 4684 if (merge_stack.len == 0) { 4685 // We still need to return a value of a next block to jump to. 4686 // For example, if we have code like 4687 // if (x) { 4688 // if (y) return else return; 4689 // } else {} 4690 // then we still need the outer to have an OpSelectionMerge and consequently 4691 // a phi node. In that case we can just return bogus, since we know that its 4692 // path will never be taken. 4693 4694 // Make sure that we are still in a block when exiting the function. 4695 // TODO: Can we get rid of that? 4696 try self.beginSpvBlock(self.spv.allocId()); 4697 const block_id_ty_id = try self.resolveType(Type.u32, .direct); 4698 return try self.spv.constUndef(block_id_ty_id); 4699 } 4700 4701 // The top-most merge actually only has a single source, the 4702 // final jump of the block, or the merge block of a sub-block, cond_br, 4703 // or loop. Therefore we just need to generate a block with a jump to the 4704 // next merge block. 4705 try self.beginSpvBlock(merge_stack[merge_stack.len - 1].merge_block); 4706 4707 // Now generate a merge ladder for the remaining merges in the stack. 4708 var incoming = ControlFlow.Structured.Block.Incoming{ 4709 .src_label = self.current_block_label, 4710 .next_block = merge_stack[merge_stack.len - 1].incoming.next_block, 4711 }; 4712 var i = merge_stack.len - 1; 4713 while (i > 0) { 4714 i -= 1; 4715 const step = merge_stack[i]; 4716 try self.func.body.emitBranch(self.spv.gpa, step.merge_block); 4717 try self.beginSpvBlock(step.merge_block); 4718 const next_block = try self.structuredNextBlock(&.{ incoming, step.incoming }); 4719 incoming = .{ 4720 .src_label = step.merge_block, 4721 .next_block = next_block, 4722 }; 4723 } 4724 4725 return incoming.next_block; 4726 }, 4727 .loop => |merge| { 4728 // Close the loop by jumping to the continue label 4729 try self.func.body.emitBranch(self.spv.gpa, block_merge_type.loop.continue_label); 4730 // For blocks we must simple merge all the incoming blocks to get the next block. 4731 try self.beginSpvBlock(merge.merge_block); 4732 return try self.structuredNextBlock(merge.merges.items); 4733 }, 4734 } 4735 } 4736 4737 fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4738 const inst_datas = self.air.instructions.items(.data); 4739 const extra = self.air.extraData(Air.Block, inst_datas[@intFromEnum(inst)].ty_pl.payload); 4740 return self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); 4741 } 4742 4743 fn lowerBlock(self: *DeclGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) !?IdRef { 4744 // In AIR, a block doesn't really define an entry point like a block, but 4745 // more like a scope that breaks can jump out of and "return" a value from. 4746 // This cannot be directly modelled in SPIR-V, so in a block instruction, 4747 // we're going to split up the current block by first generating the code 4748 // of the block, then a label, and then generate the rest of the current 4749 // ir.Block in a different SPIR-V block. 4750 4751 const mod = self.module; 4752 const ty = self.typeOfIndex(inst); 4753 const have_block_result = ty.isFnOrHasRuntimeBitsIgnoreComptime(mod); 4754 4755 const cf = switch (self.control_flow) { 4756 .structured => |*cf| cf, 4757 .unstructured => |*cf| { 4758 var block = ControlFlow.Unstructured.Block{}; 4759 defer block.incoming_blocks.deinit(self.gpa); 4760 4761 // 4 chosen as arbitrary initial capacity. 4762 try block.incoming_blocks.ensureUnusedCapacity(self.gpa, 4); 4763 4764 try cf.blocks.putNoClobber(self.gpa, inst, &block); 4765 defer assert(cf.blocks.remove(inst)); 4766 4767 try self.genBody(body); 4768 4769 // Only begin a new block if there were actually any breaks towards it. 4770 if (block.label) |label| { 4771 try self.beginSpvBlock(label); 4772 } 4773 4774 if (!have_block_result) 4775 return null; 4776 4777 assert(block.label != null); 4778 const result_id = self.spv.allocId(); 4779 const result_type_id = try self.resolveType(ty, .direct); 4780 4781 try self.func.body.emitRaw( 4782 self.spv.gpa, 4783 .OpPhi, 4784 // result type + result + variable/parent... 4785 2 + @as(u16, @intCast(block.incoming_blocks.items.len * 2)), 4786 ); 4787 self.func.body.writeOperand(spec.IdResultType, result_type_id); 4788 self.func.body.writeOperand(spec.IdRef, result_id); 4789 4790 for (block.incoming_blocks.items) |incoming| { 4791 self.func.body.writeOperand( 4792 spec.PairIdRefIdRef, 4793 .{ incoming.break_value_id, incoming.src_label }, 4794 ); 4795 } 4796 4797 return result_id; 4798 }, 4799 }; 4800 4801 const maybe_block_result_var_id = if (have_block_result) blk: { 4802 const block_result_var_id = try self.alloc(ty, .{ .storage_class = .Function }); 4803 try cf.block_results.putNoClobber(self.gpa, inst, block_result_var_id); 4804 break :blk block_result_var_id; 4805 } else null; 4806 defer if (have_block_result) assert(cf.block_results.remove(inst)); 4807 4808 const next_block = try self.genStructuredBody(.selection, body); 4809 4810 // When encountering a block instruction, we are always at least in the function's scope, 4811 // so there always has to be another entry. 4812 assert(cf.block_stack.items.len > 0); 4813 4814 // Check if the target of the branch was this current block. 4815 const this_block = try self.constInt(Type.u32, @intFromEnum(inst), .direct); 4816 const jump_to_this_block_id = self.spv.allocId(); 4817 const bool_ty_id = try self.resolveType(Type.bool, .direct); 4818 try self.func.body.emit(self.spv.gpa, .OpIEqual, .{ 4819 .id_result_type = bool_ty_id, 4820 .id_result = jump_to_this_block_id, 4821 .operand_1 = next_block, 4822 .operand_2 = this_block, 4823 }); 4824 4825 const sblock = cf.block_stack.getLast(); 4826 4827 if (ty.isNoReturn(mod)) { 4828 // If this block is noreturn, this instruction is the last of a block, 4829 // and we must simply jump to the block's merge unconditionally. 4830 try self.structuredBreak(next_block); 4831 } else { 4832 switch (sblock.*) { 4833 .selection => |*merge| { 4834 // To jump out of a selection block, push a new entry onto its merge stack and 4835 // generate a conditional branch to there and to the instructions following this block. 4836 const merge_label = self.spv.allocId(); 4837 const then_label = self.spv.allocId(); 4838 try self.func.body.emit(self.spv.gpa, .OpSelectionMerge, .{ 4839 .merge_block = merge_label, 4840 .selection_control = .{}, 4841 }); 4842 try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{ 4843 .condition = jump_to_this_block_id, 4844 .true_label = then_label, 4845 .false_label = merge_label, 4846 }); 4847 try merge.merge_stack.append(self.gpa, .{ 4848 .incoming = .{ 4849 .src_label = self.current_block_label, 4850 .next_block = next_block, 4851 }, 4852 .merge_block = merge_label, 4853 }); 4854 4855 try self.beginSpvBlock(then_label); 4856 }, 4857 .loop => |*merge| { 4858 // To jump out of a loop block, generate a conditional that exits the block 4859 // to the loop merge if the target ID is not the one of this block. 4860 const continue_label = self.spv.allocId(); 4861 try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{ 4862 .condition = jump_to_this_block_id, 4863 .true_label = continue_label, 4864 .false_label = merge.merge_block, 4865 }); 4866 try merge.merges.append(self.gpa, .{ 4867 .src_label = self.current_block_label, 4868 .next_block = next_block, 4869 }); 4870 try self.beginSpvBlock(continue_label); 4871 }, 4872 } 4873 } 4874 4875 if (maybe_block_result_var_id) |block_result_var_id| { 4876 return try self.load(ty, block_result_var_id, .{}); 4877 } 4878 4879 return null; 4880 } 4881 4882 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void { 4883 const mod = self.module; 4884 const br = self.air.instructions.items(.data)[@intFromEnum(inst)].br; 4885 const operand_ty = self.typeOf(br.operand); 4886 4887 switch (self.control_flow) { 4888 .structured => |*cf| { 4889 if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { 4890 const operand_id = try self.resolve(br.operand); 4891 const block_result_var_id = cf.block_results.get(br.block_inst).?; 4892 try self.store(operand_ty, block_result_var_id, operand_id, .{}); 4893 } 4894 4895 const next_block = try self.constInt(Type.u32, @intFromEnum(br.block_inst), .direct); 4896 try self.structuredBreak(next_block); 4897 }, 4898 .unstructured => |cf| { 4899 const block = cf.blocks.get(br.block_inst).?; 4900 if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { 4901 const operand_id = try self.resolve(br.operand); 4902 // current_block_label should not be undefined here, lest there 4903 // is a br or br_void in the function's body. 4904 try block.incoming_blocks.append(self.gpa, .{ 4905 .src_label = self.current_block_label, 4906 .break_value_id = operand_id, 4907 }); 4908 } 4909 4910 if (block.label == null) { 4911 block.label = self.spv.allocId(); 4912 } 4913 4914 try self.func.body.emitBranch(self.spv.gpa, block.label.?); 4915 }, 4916 } 4917 } 4918 4919 fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void { 4920 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 4921 const cond_br = self.air.extraData(Air.CondBr, pl_op.payload); 4922 const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra[cond_br.end..][0..cond_br.data.then_body_len]); 4923 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[cond_br.end + then_body.len ..][0..cond_br.data.else_body_len]); 4924 const condition_id = try self.resolve(pl_op.operand); 4925 4926 const then_label = self.spv.allocId(); 4927 const else_label = self.spv.allocId(); 4928 4929 switch (self.control_flow) { 4930 .structured => { 4931 const merge_label = self.spv.allocId(); 4932 4933 try self.func.body.emit(self.spv.gpa, .OpSelectionMerge, .{ 4934 .merge_block = merge_label, 4935 .selection_control = .{}, 4936 }); 4937 try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{ 4938 .condition = condition_id, 4939 .true_label = then_label, 4940 .false_label = else_label, 4941 }); 4942 4943 try self.beginSpvBlock(then_label); 4944 const then_next = try self.genStructuredBody(.selection, then_body); 4945 const then_incoming = ControlFlow.Structured.Block.Incoming{ 4946 .src_label = self.current_block_label, 4947 .next_block = then_next, 4948 }; 4949 try self.func.body.emitBranch(self.spv.gpa, merge_label); 4950 4951 try self.beginSpvBlock(else_label); 4952 const else_next = try self.genStructuredBody(.selection, else_body); 4953 const else_incoming = ControlFlow.Structured.Block.Incoming{ 4954 .src_label = self.current_block_label, 4955 .next_block = else_next, 4956 }; 4957 try self.func.body.emitBranch(self.spv.gpa, merge_label); 4958 4959 try self.beginSpvBlock(merge_label); 4960 const next_block = try self.structuredNextBlock(&.{ then_incoming, else_incoming }); 4961 4962 try self.structuredBreak(next_block); 4963 }, 4964 .unstructured => { 4965 try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{ 4966 .condition = condition_id, 4967 .true_label = then_label, 4968 .false_label = else_label, 4969 }); 4970 4971 try self.beginSpvBlock(then_label); 4972 try self.genBody(then_body); 4973 try self.beginSpvBlock(else_label); 4974 try self.genBody(else_body); 4975 }, 4976 } 4977 } 4978 4979 fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void { 4980 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 4981 const loop = self.air.extraData(Air.Block, ty_pl.payload); 4982 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[loop.end..][0..loop.data.body_len]); 4983 4984 const body_label = self.spv.allocId(); 4985 4986 switch (self.control_flow) { 4987 .structured => { 4988 const header_label = self.spv.allocId(); 4989 const merge_label = self.spv.allocId(); 4990 const continue_label = self.spv.allocId(); 4991 4992 // The back-edge must point to the loop header, so generate a separate block for the 4993 // loop header so that we don't accidentally include some instructions from there 4994 // in the loop. 4995 try self.func.body.emitBranch(self.spv.gpa, header_label); 4996 try self.beginSpvBlock(header_label); 4997 4998 // Emit loop header and jump to loop body 4999 try self.func.body.emit(self.spv.gpa, .OpLoopMerge, .{ 5000 .merge_block = merge_label, 5001 .continue_target = continue_label, 5002 .loop_control = .{}, 5003 }); 5004 try self.func.body.emitBranch(self.spv.gpa, body_label); 5005 5006 try self.beginSpvBlock(body_label); 5007 5008 const next_block = try self.genStructuredBody(.{ .loop = .{ 5009 .merge_label = merge_label, 5010 .continue_label = continue_label, 5011 } }, body); 5012 try self.structuredBreak(next_block); 5013 5014 try self.beginSpvBlock(continue_label); 5015 try self.func.body.emitBranch(self.spv.gpa, header_label); 5016 }, 5017 .unstructured => { 5018 try self.func.body.emitBranch(self.spv.gpa, body_label); 5019 try self.beginSpvBlock(body_label); 5020 try self.genBody(body); 5021 try self.func.body.emitBranch(self.spv.gpa, body_label); 5022 }, 5023 } 5024 } 5025 5026 fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5027 const mod = self.module; 5028 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 5029 const ptr_ty = self.typeOf(ty_op.operand); 5030 const elem_ty = self.typeOfIndex(inst); 5031 const operand = try self.resolve(ty_op.operand); 5032 if (!ptr_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null; 5033 5034 return try self.load(elem_ty, operand, .{ .is_volatile = ptr_ty.isVolatilePtr(mod) }); 5035 } 5036 5037 fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void { 5038 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 5039 const ptr_ty = self.typeOf(bin_op.lhs); 5040 const elem_ty = ptr_ty.childType(self.module); 5041 const ptr = try self.resolve(bin_op.lhs); 5042 const value = try self.resolve(bin_op.rhs); 5043 5044 try self.store(elem_ty, ptr, value, .{ .is_volatile = ptr_ty.isVolatilePtr(self.module) }); 5045 } 5046 5047 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void { 5048 const operand = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 5049 const ret_ty = self.typeOf(operand); 5050 const mod = self.module; 5051 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) { 5052 const decl = mod.declPtr(self.decl_index); 5053 const fn_info = mod.typeToFunc(decl.typeOf(mod)).?; 5054 if (Type.fromInterned(fn_info.return_type).isError(mod)) { 5055 // Functions with an empty error set are emitted with an error code 5056 // return type and return zero so they can be function pointers coerced 5057 // to functions that return anyerror. 5058 const no_err_id = try self.constInt(Type.anyerror, 0, .direct); 5059 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id }); 5060 } else { 5061 return try self.func.body.emit(self.spv.gpa, .OpReturn, {}); 5062 } 5063 } 5064 5065 const operand_id = try self.resolve(operand); 5066 try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = operand_id }); 5067 } 5068 5069 fn airRetLoad(self: *DeclGen, inst: Air.Inst.Index) !void { 5070 const mod = self.module; 5071 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 5072 const ptr_ty = self.typeOf(un_op); 5073 const ret_ty = ptr_ty.childType(mod); 5074 5075 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) { 5076 const decl = mod.declPtr(self.decl_index); 5077 const fn_info = mod.typeToFunc(decl.typeOf(mod)).?; 5078 if (Type.fromInterned(fn_info.return_type).isError(mod)) { 5079 // Functions with an empty error set are emitted with an error code 5080 // return type and return zero so they can be function pointers coerced 5081 // to functions that return anyerror. 5082 const no_err_id = try self.constInt(Type.anyerror, 0, .direct); 5083 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id }); 5084 } else { 5085 return try self.func.body.emit(self.spv.gpa, .OpReturn, {}); 5086 } 5087 } 5088 5089 const ptr = try self.resolve(un_op); 5090 const value = try self.load(ret_ty, ptr, .{ .is_volatile = ptr_ty.isVolatilePtr(mod) }); 5091 try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ 5092 .value = value, 5093 }); 5094 } 5095 5096 fn airTry(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5097 const mod = self.module; 5098 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 5099 const err_union_id = try self.resolve(pl_op.operand); 5100 const extra = self.air.extraData(Air.Try, pl_op.payload); 5101 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); 5102 5103 const err_union_ty = self.typeOf(pl_op.operand); 5104 const payload_ty = self.typeOfIndex(inst); 5105 5106 const bool_ty_id = try self.resolveType(Type.bool, .direct); 5107 5108 const eu_layout = self.errorUnionLayout(payload_ty); 5109 5110 if (!err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { 5111 const err_id = if (eu_layout.payload_has_bits) 5112 try self.extractField(Type.anyerror, err_union_id, eu_layout.errorFieldIndex()) 5113 else 5114 err_union_id; 5115 5116 const zero_id = try self.constInt(Type.anyerror, 0, .direct); 5117 const is_err_id = self.spv.allocId(); 5118 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ 5119 .id_result_type = bool_ty_id, 5120 .id_result = is_err_id, 5121 .operand_1 = err_id, 5122 .operand_2 = zero_id, 5123 }); 5124 5125 // When there is an error, we must evaluate `body`. Otherwise we must continue 5126 // with the current body. 5127 // Just generate a new block here, then generate a new block inline for the remainder of the body. 5128 5129 const err_block = self.spv.allocId(); 5130 const ok_block = self.spv.allocId(); 5131 5132 switch (self.control_flow) { 5133 .structured => { 5134 // According to AIR documentation, this block is guaranteed 5135 // to not break and end in a return instruction. Thus, 5136 // for structured control flow, we can just naively use 5137 // the ok block as the merge block here. 5138 try self.func.body.emit(self.spv.gpa, .OpSelectionMerge, .{ 5139 .merge_block = ok_block, 5140 .selection_control = .{}, 5141 }); 5142 }, 5143 .unstructured => {}, 5144 } 5145 5146 try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{ 5147 .condition = is_err_id, 5148 .true_label = err_block, 5149 .false_label = ok_block, 5150 }); 5151 5152 try self.beginSpvBlock(err_block); 5153 try self.genBody(body); 5154 5155 try self.beginSpvBlock(ok_block); 5156 } 5157 5158 if (!eu_layout.payload_has_bits) { 5159 return null; 5160 } 5161 5162 // Now just extract the payload, if required. 5163 return try self.extractField(payload_ty, err_union_id, eu_layout.payloadFieldIndex()); 5164 } 5165 5166 fn airErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5167 const mod = self.module; 5168 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 5169 const operand_id = try self.resolve(ty_op.operand); 5170 const err_union_ty = self.typeOf(ty_op.operand); 5171 const err_ty_id = try self.resolveType(Type.anyerror, .direct); 5172 5173 if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { 5174 // No error possible, so just return undefined. 5175 return try self.spv.constUndef(err_ty_id); 5176 } 5177 5178 const payload_ty = err_union_ty.errorUnionPayload(mod); 5179 const eu_layout = self.errorUnionLayout(payload_ty); 5180 5181 if (!eu_layout.payload_has_bits) { 5182 // If no payload, error union is represented by error set. 5183 return operand_id; 5184 } 5185 5186 return try self.extractField(Type.anyerror, operand_id, eu_layout.errorFieldIndex()); 5187 } 5188 5189 fn airErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5190 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 5191 const operand_id = try self.resolve(ty_op.operand); 5192 const payload_ty = self.typeOfIndex(inst); 5193 const eu_layout = self.errorUnionLayout(payload_ty); 5194 5195 if (!eu_layout.payload_has_bits) { 5196 return null; // No error possible. 5197 } 5198 5199 return try self.extractField(payload_ty, operand_id, eu_layout.payloadFieldIndex()); 5200 } 5201 5202 fn airWrapErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5203 const mod = self.module; 5204 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 5205 const err_union_ty = self.typeOfIndex(inst); 5206 const payload_ty = err_union_ty.errorUnionPayload(mod); 5207 const operand_id = try self.resolve(ty_op.operand); 5208 const eu_layout = self.errorUnionLayout(payload_ty); 5209 5210 if (!eu_layout.payload_has_bits) { 5211 return operand_id; 5212 } 5213 5214 const payload_ty_id = try self.resolveType(payload_ty, .indirect); 5215 5216 var members: [2]IdRef = undefined; 5217 members[eu_layout.errorFieldIndex()] = operand_id; 5218 members[eu_layout.payloadFieldIndex()] = try self.spv.constUndef(payload_ty_id); 5219 5220 var types: [2]Type = undefined; 5221 types[eu_layout.errorFieldIndex()] = Type.anyerror; 5222 types[eu_layout.payloadFieldIndex()] = payload_ty; 5223 5224 return try self.constructStruct(err_union_ty, &types, &members); 5225 } 5226 5227 fn airWrapErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5228 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 5229 const err_union_ty = self.typeOfIndex(inst); 5230 const operand_id = try self.resolve(ty_op.operand); 5231 const payload_ty = self.typeOf(ty_op.operand); 5232 const eu_layout = self.errorUnionLayout(payload_ty); 5233 5234 if (!eu_layout.payload_has_bits) { 5235 return try self.constInt(Type.anyerror, 0, .direct); 5236 } 5237 5238 var members: [2]IdRef = undefined; 5239 members[eu_layout.errorFieldIndex()] = try self.constInt(Type.anyerror, 0, .direct); 5240 members[eu_layout.payloadFieldIndex()] = try self.convertToIndirect(payload_ty, operand_id); 5241 5242 var types: [2]Type = undefined; 5243 types[eu_layout.errorFieldIndex()] = Type.anyerror; 5244 types[eu_layout.payloadFieldIndex()] = payload_ty; 5245 5246 return try self.constructStruct(err_union_ty, &types, &members); 5247 } 5248 5249 fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, is_pointer: bool, pred: enum { is_null, is_non_null }) !?IdRef { 5250 const mod = self.module; 5251 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 5252 const operand_id = try self.resolve(un_op); 5253 const operand_ty = self.typeOf(un_op); 5254 const optional_ty = if (is_pointer) operand_ty.childType(mod) else operand_ty; 5255 const payload_ty = optional_ty.optionalChild(mod); 5256 5257 const bool_ty_id = try self.resolveType(Type.bool, .direct); 5258 5259 if (optional_ty.optionalReprIsPayload(mod)) { 5260 // Pointer payload represents nullability: pointer or slice. 5261 const loaded_id = if (is_pointer) 5262 try self.load(optional_ty, operand_id, .{}) 5263 else 5264 operand_id; 5265 5266 const ptr_ty = if (payload_ty.isSlice(mod)) 5267 payload_ty.slicePtrFieldType(mod) 5268 else 5269 payload_ty; 5270 5271 const ptr_id = if (payload_ty.isSlice(mod)) 5272 try self.extractField(ptr_ty, loaded_id, 0) 5273 else 5274 loaded_id; 5275 5276 const payload_ty_id = try self.resolveType(ptr_ty, .direct); 5277 const null_id = try self.spv.constNull(payload_ty_id); 5278 const op: std.math.CompareOperator = switch (pred) { 5279 .is_null => .eq, 5280 .is_non_null => .neq, 5281 }; 5282 return try self.cmp(op, Type.bool, ptr_ty, ptr_id, null_id); 5283 } 5284 5285 const is_non_null_id = blk: { 5286 if (is_pointer) { 5287 if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { 5288 const storage_class = self.spvStorageClass(operand_ty.ptrAddressSpace(mod)); 5289 const bool_ptr_ty_id = try self.ptrType(Type.bool, storage_class); 5290 const tag_ptr_id = try self.accessChain(bool_ptr_ty_id, operand_id, &.{1}); 5291 break :blk try self.load(Type.bool, tag_ptr_id, .{}); 5292 } 5293 5294 break :blk try self.load(Type.bool, operand_id, .{}); 5295 } 5296 5297 break :blk if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) 5298 try self.extractField(Type.bool, operand_id, 1) 5299 else 5300 // Optional representation is bool indicating whether the optional is set 5301 // Optionals with no payload are represented as an (indirect) bool, so convert 5302 // it back to the direct bool here. 5303 try self.convertToDirect(Type.bool, operand_id); 5304 }; 5305 5306 return switch (pred) { 5307 .is_null => blk: { 5308 // Invert condition 5309 const result_id = self.spv.allocId(); 5310 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{ 5311 .id_result_type = bool_ty_id, 5312 .id_result = result_id, 5313 .operand = is_non_null_id, 5314 }); 5315 break :blk result_id; 5316 }, 5317 .is_non_null => is_non_null_id, 5318 }; 5319 } 5320 5321 fn airIsErr(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_err, is_non_err }) !?IdRef { 5322 const mod = self.module; 5323 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 5324 const operand_id = try self.resolve(un_op); 5325 const err_union_ty = self.typeOf(un_op); 5326 5327 if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { 5328 return try self.constBool(pred == .is_non_err, .direct); 5329 } 5330 5331 const payload_ty = err_union_ty.errorUnionPayload(mod); 5332 const eu_layout = self.errorUnionLayout(payload_ty); 5333 const bool_ty_id = try self.resolveType(Type.bool, .direct); 5334 5335 const error_id = if (!eu_layout.payload_has_bits) 5336 operand_id 5337 else 5338 try self.extractField(Type.anyerror, operand_id, eu_layout.errorFieldIndex()); 5339 5340 const result_id = self.spv.allocId(); 5341 const operands = .{ 5342 .id_result_type = bool_ty_id, 5343 .id_result = result_id, 5344 .operand_1 = error_id, 5345 .operand_2 = try self.constInt(Type.anyerror, 0, .direct), 5346 }; 5347 switch (pred) { 5348 .is_err => try self.func.body.emit(self.spv.gpa, .OpINotEqual, operands), 5349 .is_non_err => try self.func.body.emit(self.spv.gpa, .OpIEqual, operands), 5350 } 5351 return result_id; 5352 } 5353 5354 fn airUnwrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5355 const mod = self.module; 5356 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 5357 const operand_id = try self.resolve(ty_op.operand); 5358 const optional_ty = self.typeOf(ty_op.operand); 5359 const payload_ty = self.typeOfIndex(inst); 5360 5361 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return null; 5362 5363 if (optional_ty.optionalReprIsPayload(mod)) { 5364 return operand_id; 5365 } 5366 5367 return try self.extractField(payload_ty, operand_id, 0); 5368 } 5369 5370 fn airUnwrapOptionalPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5371 const mod = self.module; 5372 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 5373 const operand_id = try self.resolve(ty_op.operand); 5374 const operand_ty = self.typeOf(ty_op.operand); 5375 const optional_ty = operand_ty.childType(mod); 5376 const payload_ty = optional_ty.optionalChild(mod); 5377 const result_ty = self.typeOfIndex(inst); 5378 const result_ty_id = try self.resolveType(result_ty, .direct); 5379 5380 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { 5381 // There is no payload, but we still need to return a valid pointer. 5382 // We can just return anything here, so just return a pointer to the operand. 5383 return try self.bitCast(result_ty, operand_ty, operand_id); 5384 } 5385 5386 if (optional_ty.optionalReprIsPayload(mod)) { 5387 // They are the same value. 5388 return try self.bitCast(result_ty, operand_ty, operand_id); 5389 } 5390 5391 return try self.accessChain(result_ty_id, operand_id, &.{0}); 5392 } 5393 5394 fn airWrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5395 const mod = self.module; 5396 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 5397 const payload_ty = self.typeOf(ty_op.operand); 5398 5399 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { 5400 return try self.constBool(true, .indirect); 5401 } 5402 5403 const operand_id = try self.resolve(ty_op.operand); 5404 5405 const optional_ty = self.typeOfIndex(inst); 5406 if (optional_ty.optionalReprIsPayload(mod)) { 5407 return operand_id; 5408 } 5409 5410 const payload_id = try self.convertToIndirect(payload_ty, operand_id); 5411 const members = [_]IdRef{ payload_id, try self.constBool(true, .indirect) }; 5412 const types = [_]Type{ payload_ty, Type.bool }; 5413 return try self.constructStruct(optional_ty, &types, &members); 5414 } 5415 5416 fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { 5417 const mod = self.module; 5418 const target = self.getTarget(); 5419 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 5420 const cond_ty = self.typeOf(pl_op.operand); 5421 const cond = try self.resolve(pl_op.operand); 5422 var cond_indirect = try self.convertToIndirect(cond_ty, cond); 5423 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); 5424 5425 const cond_words: u32 = switch (cond_ty.zigTypeTag(mod)) { 5426 .Bool, .ErrorSet => 1, 5427 .Int => blk: { 5428 const bits = cond_ty.intInfo(mod).bits; 5429 const backing_bits = self.backingIntBits(bits) orelse { 5430 return self.todo("implement composite int switch", .{}); 5431 }; 5432 break :blk if (backing_bits <= 32) 1 else 2; 5433 }, 5434 .Enum => blk: { 5435 const int_ty = cond_ty.intTagType(mod); 5436 const int_info = int_ty.intInfo(mod); 5437 const backing_bits = self.backingIntBits(int_info.bits) orelse { 5438 return self.todo("implement composite int switch", .{}); 5439 }; 5440 break :blk if (backing_bits <= 32) 1 else 2; 5441 }, 5442 .Pointer => blk: { 5443 cond_indirect = try self.intFromPtr(cond_indirect); 5444 break :blk target.ptrBitWidth() / 32; 5445 }, 5446 // TODO: Figure out which types apply here, and work around them as we can only do integers. 5447 else => return self.todo("implement switch for type {s}", .{@tagName(cond_ty.zigTypeTag(mod))}), 5448 }; 5449 5450 const num_cases = switch_br.data.cases_len; 5451 5452 // Compute the total number of arms that we need. 5453 // Zig switches are grouped by condition, so we need to loop through all of them 5454 const num_conditions = blk: { 5455 var extra_index: usize = switch_br.end; 5456 var num_conditions: u32 = 0; 5457 for (0..num_cases) |_| { 5458 const case = self.air.extraData(Air.SwitchBr.Case, extra_index); 5459 const case_body = self.air.extra[case.end + case.data.items_len ..][0..case.data.body_len]; 5460 extra_index = case.end + case.data.items_len + case_body.len; 5461 num_conditions += case.data.items_len; 5462 } 5463 break :blk num_conditions; 5464 }; 5465 5466 // First, pre-allocate the labels for the cases. 5467 const case_labels = self.spv.allocIds(num_cases); 5468 // We always need the default case - if zig has none, we will generate unreachable there. 5469 const default = self.spv.allocId(); 5470 5471 const merge_label = switch (self.control_flow) { 5472 .structured => self.spv.allocId(), 5473 .unstructured => null, 5474 }; 5475 5476 if (self.control_flow == .structured) { 5477 try self.func.body.emit(self.spv.gpa, .OpSelectionMerge, .{ 5478 .merge_block = merge_label.?, 5479 .selection_control = .{}, 5480 }); 5481 } 5482 5483 // Emit the instruction before generating the blocks. 5484 try self.func.body.emitRaw(self.spv.gpa, .OpSwitch, 2 + (cond_words + 1) * num_conditions); 5485 self.func.body.writeOperand(IdRef, cond_indirect); 5486 self.func.body.writeOperand(IdRef, default); 5487 5488 // Emit each of the cases 5489 { 5490 var extra_index: usize = switch_br.end; 5491 for (0..num_cases) |case_i| { 5492 // SPIR-V needs a literal here, which' width depends on the case condition. 5493 const case = self.air.extraData(Air.SwitchBr.Case, extra_index); 5494 const items: []const Air.Inst.Ref = @ptrCast(self.air.extra[case.end..][0..case.data.items_len]); 5495 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; 5496 extra_index = case.end + case.data.items_len + case_body.len; 5497 5498 const label = case_labels.at(case_i); 5499 5500 for (items) |item| { 5501 const value = (try self.air.value(item, mod)) orelse unreachable; 5502 const int_val: u64 = switch (cond_ty.zigTypeTag(mod)) { 5503 .Bool, .Int => if (cond_ty.isSignedInt(mod)) @bitCast(value.toSignedInt(mod)) else value.toUnsignedInt(mod), 5504 .Enum => blk: { 5505 // TODO: figure out of cond_ty is correct (something with enum literals) 5506 break :blk (try value.intFromEnum(cond_ty, mod)).toUnsignedInt(mod); // TODO: composite integer constants 5507 }, 5508 .ErrorSet => value.getErrorInt(mod), 5509 .Pointer => value.toUnsignedInt(mod), 5510 else => unreachable, 5511 }; 5512 const int_lit: spec.LiteralContextDependentNumber = switch (cond_words) { 5513 1 => .{ .uint32 = @intCast(int_val) }, 5514 2 => .{ .uint64 = int_val }, 5515 else => unreachable, 5516 }; 5517 self.func.body.writeOperand(spec.LiteralContextDependentNumber, int_lit); 5518 self.func.body.writeOperand(IdRef, label); 5519 } 5520 } 5521 } 5522 5523 var incoming_structured_blocks = std.ArrayListUnmanaged(ControlFlow.Structured.Block.Incoming){}; 5524 defer incoming_structured_blocks.deinit(self.gpa); 5525 5526 if (self.control_flow == .structured) { 5527 try incoming_structured_blocks.ensureUnusedCapacity(self.gpa, num_cases + 1); 5528 } 5529 5530 // Now, finally, we can start emitting each of the cases. 5531 var extra_index: usize = switch_br.end; 5532 for (0..num_cases) |case_i| { 5533 const case = self.air.extraData(Air.SwitchBr.Case, extra_index); 5534 const items: []const Air.Inst.Ref = @ptrCast(self.air.extra[case.end..][0..case.data.items_len]); 5535 const case_body: []const Air.Inst.Index = @ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]); 5536 extra_index = case.end + case.data.items_len + case_body.len; 5537 5538 const label = case_labels.at(case_i); 5539 5540 try self.beginSpvBlock(label); 5541 5542 switch (self.control_flow) { 5543 .structured => { 5544 const next_block = try self.genStructuredBody(.selection, case_body); 5545 incoming_structured_blocks.appendAssumeCapacity(.{ 5546 .src_label = self.current_block_label, 5547 .next_block = next_block, 5548 }); 5549 try self.func.body.emitBranch(self.spv.gpa, merge_label.?); 5550 }, 5551 .unstructured => { 5552 try self.genBody(case_body); 5553 }, 5554 } 5555 } 5556 5557 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]); 5558 try self.beginSpvBlock(default); 5559 if (else_body.len != 0) { 5560 switch (self.control_flow) { 5561 .structured => { 5562 const next_block = try self.genStructuredBody(.selection, else_body); 5563 incoming_structured_blocks.appendAssumeCapacity(.{ 5564 .src_label = self.current_block_label, 5565 .next_block = next_block, 5566 }); 5567 try self.func.body.emitBranch(self.spv.gpa, merge_label.?); 5568 }, 5569 .unstructured => { 5570 try self.genBody(else_body); 5571 }, 5572 } 5573 } else { 5574 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); 5575 } 5576 5577 if (self.control_flow == .structured) { 5578 try self.beginSpvBlock(merge_label.?); 5579 const next_block = try self.structuredNextBlock(incoming_structured_blocks.items); 5580 try self.structuredBreak(next_block); 5581 } 5582 } 5583 5584 fn airUnreach(self: *DeclGen) !void { 5585 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); 5586 } 5587 5588 fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void { 5589 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; 5590 const mod = self.module; 5591 const decl = mod.declPtr(self.decl_index); 5592 const path = decl.getFileScope(mod).sub_file_path; 5593 try self.func.body.emit(self.spv.gpa, .OpLine, .{ 5594 .file = try self.spv.resolveString(path), 5595 .line = self.base_line + dbg_stmt.line + 1, 5596 .column = dbg_stmt.column + 1, 5597 }); 5598 } 5599 5600 fn airDbgInlineBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5601 const mod = self.module; 5602 const inst_datas = self.air.instructions.items(.data); 5603 const extra = self.air.extraData(Air.DbgInlineBlock, inst_datas[@intFromEnum(inst)].ty_pl.payload); 5604 const decl = mod.funcOwnerDeclPtr(extra.data.func); 5605 const old_base_line = self.base_line; 5606 defer self.base_line = old_base_line; 5607 self.base_line = decl.src_line; 5608 return self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); 5609 } 5610 5611 fn airDbgVar(self: *DeclGen, inst: Air.Inst.Index) !void { 5612 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 5613 const target_id = try self.resolve(pl_op.operand); 5614 const name = self.air.nullTerminatedString(pl_op.payload); 5615 try self.spv.debugName(target_id, name); 5616 } 5617 5618 fn airAssembly(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 5619 const mod = self.module; 5620 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 5621 const extra = self.air.extraData(Air.Asm, ty_pl.payload); 5622 5623 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; 5624 const clobbers_len: u31 = @truncate(extra.data.flags); 5625 5626 if (!is_volatile and self.liveness.isUnused(inst)) return null; 5627 5628 var extra_i: usize = extra.end; 5629 const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.outputs_len]); 5630 extra_i += outputs.len; 5631 const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.inputs_len]); 5632 extra_i += inputs.len; 5633 5634 if (outputs.len > 1) { 5635 return self.todo("implement inline asm with more than 1 output", .{}); 5636 } 5637 5638 var output_extra_i = extra_i; 5639 for (outputs) |output| { 5640 if (output != .none) { 5641 return self.todo("implement inline asm with non-returned output", .{}); 5642 } 5643 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 5644 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 5645 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 5646 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 5647 // TODO: Record output and use it somewhere. 5648 } 5649 5650 var input_extra_i = extra_i; 5651 for (inputs) |input| { 5652 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 5653 const constraint = std.mem.sliceTo(extra_bytes, 0); 5654 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 5655 // This equation accounts for the fact that even if we have exactly 4 bytes 5656 // for the string, we still use the next u32 for the null terminator. 5657 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 5658 // TODO: Record input and use it somewhere. 5659 _ = input; 5660 } 5661 5662 { 5663 var clobber_i: u32 = 0; 5664 while (clobber_i < clobbers_len) : (clobber_i += 1) { 5665 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 5666 extra_i += clobber.len / 4 + 1; 5667 // TODO: Record clobber and use it somewhere. 5668 } 5669 } 5670 5671 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; 5672 5673 var as = SpvAssembler{ 5674 .gpa = self.gpa, 5675 .src = asm_source, 5676 .spv = self.spv, 5677 .func = &self.func, 5678 }; 5679 defer as.deinit(); 5680 5681 for (inputs) |input| { 5682 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[input_extra_i..]); 5683 const constraint = std.mem.sliceTo(extra_bytes, 0); 5684 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 5685 // This equation accounts for the fact that even if we have exactly 4 bytes 5686 // for the string, we still use the next u32 for the null terminator. 5687 input_extra_i += (constraint.len + name.len + (2 + 3)) / 4; 5688 5689 const value = try self.resolve(input); 5690 try as.value_map.put(as.gpa, name, .{ .value = value }); 5691 } 5692 5693 as.assemble() catch |err| switch (err) { 5694 error.AssembleFail => { 5695 // TODO: For now the compiler only supports a single error message per decl, 5696 // so to translate the possible multiple errors from the assembler, emit 5697 // them as notes here. 5698 // TODO: Translate proper error locations. 5699 assert(as.errors.items.len != 0); 5700 assert(self.error_msg == null); 5701 const src_loc = self.module.declPtr(self.decl_index).srcLoc(mod); 5702 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{}); 5703 const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len); 5704 5705 // Sub-scope to prevent `return error.CodegenFail` from running the errdefers. 5706 { 5707 errdefer self.module.gpa.free(notes); 5708 var i: usize = 0; 5709 errdefer for (notes[0..i]) |*note| { 5710 note.deinit(self.module.gpa); 5711 }; 5712 5713 while (i < as.errors.items.len) : (i += 1) { 5714 notes[i] = try Module.ErrorMsg.init(self.module.gpa, src_loc, "{s}", .{as.errors.items[i].msg}); 5715 } 5716 } 5717 self.error_msg.?.notes = notes; 5718 return error.CodegenFail; 5719 }, 5720 else => |others| return others, 5721 }; 5722 5723 for (outputs) |output| { 5724 _ = output; 5725 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[output_extra_i..]); 5726 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[output_extra_i..]), 0); 5727 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 5728 output_extra_i += (constraint.len + name.len + (2 + 3)) / 4; 5729 5730 const result = as.value_map.get(name) orelse return { 5731 return self.fail("invalid asm output '{s}'", .{name}); 5732 }; 5733 5734 switch (result) { 5735 .just_declared, .unresolved_forward_reference => unreachable, 5736 .ty => return self.fail("cannot return spir-v type as value from assembly", .{}), 5737 .value => |ref| return ref, 5738 } 5739 5740 // TODO: Multiple results 5741 // TODO: Check that the output type from assembly is the same as the type actually expected by Zig. 5742 } 5743 5744 return null; 5745 } 5746 5747 fn airCall(self: *DeclGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !?IdRef { 5748 _ = modifier; 5749 5750 const mod = self.module; 5751 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 5752 const extra = self.air.extraData(Air.Call, pl_op.payload); 5753 const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); 5754 const callee_ty = self.typeOf(pl_op.operand); 5755 const zig_fn_ty = switch (callee_ty.zigTypeTag(mod)) { 5756 .Fn => callee_ty, 5757 .Pointer => return self.fail("cannot call function pointers", .{}), 5758 else => unreachable, 5759 }; 5760 const fn_info = mod.typeToFunc(zig_fn_ty).?; 5761 const return_type = fn_info.return_type; 5762 5763 const result_type_id = try self.resolveFnReturnType(Type.fromInterned(return_type)); 5764 const result_id = self.spv.allocId(); 5765 const callee_id = try self.resolve(pl_op.operand); 5766 5767 comptime assert(zig_call_abi_ver == 3); 5768 const params = try self.gpa.alloc(spec.IdRef, args.len); 5769 defer self.gpa.free(params); 5770 var n_params: usize = 0; 5771 for (args) |arg| { 5772 // Note: resolve() might emit instructions, so we need to call it 5773 // before starting to emit OpFunctionCall instructions. Hence the 5774 // temporary params buffer. 5775 const arg_ty = self.typeOf(arg); 5776 if (!arg_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; 5777 const arg_id = try self.resolve(arg); 5778 5779 params[n_params] = arg_id; 5780 n_params += 1; 5781 } 5782 5783 try self.func.body.emit(self.spv.gpa, .OpFunctionCall, .{ 5784 .id_result_type = result_type_id, 5785 .id_result = result_id, 5786 .function = callee_id, 5787 .id_ref_3 = params[0..n_params], 5788 }); 5789 5790 if (return_type == .noreturn_type) { 5791 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); 5792 } 5793 5794 if (self.liveness.isUnused(inst) or !Type.fromInterned(return_type).hasRuntimeBitsIgnoreComptime(mod)) { 5795 return null; 5796 } 5797 5798 return result_id; 5799 } 5800 5801 fn typeOf(self: *DeclGen, inst: Air.Inst.Ref) Type { 5802 const mod = self.module; 5803 return self.air.typeOf(inst, &mod.intern_pool); 5804 } 5805 5806 fn typeOfIndex(self: *DeclGen, inst: Air.Inst.Index) Type { 5807 const mod = self.module; 5808 return self.air.typeOfIndex(inst, &mod.intern_pool); 5809 } 5810 };