blob ded73d6a (195237B) - 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").Value; 11 const LazySrcLoc = Module.LazySrcLoc; 12 const Air = @import("../Air.zig"); 13 const Zir = @import("../Zir.zig"); 14 const Liveness = @import("../Liveness.zig"); 15 const InternPool = @import("../InternPool.zig"); 16 17 const spec = @import("spirv/spec.zig"); 18 const Opcode = spec.Opcode; 19 const Word = spec.Word; 20 const IdRef = spec.IdRef; 21 const IdResult = spec.IdResult; 22 const IdResultType = spec.IdResultType; 23 const StorageClass = spec.StorageClass; 24 25 const SpvModule = @import("spirv/Module.zig"); 26 const CacheRef = SpvModule.CacheRef; 27 const CacheString = SpvModule.CacheString; 28 29 const SpvSection = @import("spirv/Section.zig"); 30 const SpvAssembler = @import("spirv/Assembler.zig"); 31 32 const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef); 33 34 /// We want to store some extra facts about types as mapped from Zig to SPIR-V. 35 /// This structure is used to keep that extra information, as well as 36 /// the cached reference to the type. 37 const SpvTypeInfo = struct { 38 ty_ref: CacheRef, 39 }; 40 41 const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, SpvTypeInfo); 42 43 const IncomingBlock = struct { 44 src_label_id: IdRef, 45 break_value_id: IdRef, 46 }; 47 48 const Block = struct { 49 label_id: ?IdRef, 50 incoming_blocks: std.ArrayListUnmanaged(IncomingBlock), 51 }; 52 53 const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, *Block); 54 55 /// This structure holds information that is relevant to the entire compilation, 56 /// in contrast to `DeclGen`, which only holds relevant information about a 57 /// single decl. 58 pub const Object = struct { 59 /// A general-purpose allocator that can be used for any allocation for this Object. 60 gpa: Allocator, 61 62 /// the SPIR-V module that represents the final binary. 63 spv: SpvModule, 64 65 /// The Zig module that this object file is generated for. 66 /// A map of Zig decl indices to SPIR-V decl indices. 67 decl_link: std.AutoHashMapUnmanaged(Decl.Index, SpvModule.Decl.Index) = .{}, 68 69 /// A map of Zig InternPool indices for anonymous decls to SPIR-V decl indices. 70 anon_decl_link: std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index) = .{}, 71 72 /// A map that maps AIR intern pool indices to SPIR-V cache references (which 73 /// is basically the same thing except for SPIR-V). 74 /// This map is typically only used for structures that are deemed heavy enough 75 /// that it is worth to store them here. The SPIR-V module also interns types, 76 /// and so the main purpose of this map is to avoid recomputation and to 77 /// cache extra information about the type rather than to aid in validity 78 /// of the SPIR-V module. 79 type_map: TypeMap = .{}, 80 81 pub fn init(gpa: Allocator) Object { 82 return .{ 83 .gpa = gpa, 84 .spv = SpvModule.init(gpa), 85 }; 86 } 87 88 pub fn deinit(self: *Object) void { 89 self.spv.deinit(); 90 self.decl_link.deinit(self.gpa); 91 self.anon_decl_link.deinit(self.gpa); 92 self.type_map.deinit(self.gpa); 93 } 94 95 fn genDecl( 96 self: *Object, 97 mod: *Module, 98 decl_index: Decl.Index, 99 air: Air, 100 liveness: Liveness, 101 ) !void { 102 var decl_gen = DeclGen{ 103 .gpa = self.gpa, 104 .object = self, 105 .module = mod, 106 .spv = &self.spv, 107 .decl_index = decl_index, 108 .air = air, 109 .liveness = liveness, 110 .type_map = &self.type_map, 111 .current_block_label_id = undefined, 112 }; 113 defer decl_gen.deinit(); 114 115 decl_gen.genDecl() catch |err| switch (err) { 116 error.CodegenFail => { 117 try mod.failed_decls.put(mod.gpa, decl_index, decl_gen.error_msg.?); 118 }, 119 else => |other| { 120 // There might be an error that happened *after* self.error_msg 121 // was already allocated, so be sure to free it. 122 if (decl_gen.error_msg) |error_msg| { 123 error_msg.deinit(mod.gpa); 124 } 125 126 return other; 127 }, 128 }; 129 } 130 131 pub fn updateFunc( 132 self: *Object, 133 mod: *Module, 134 func_index: InternPool.Index, 135 air: Air, 136 liveness: Liveness, 137 ) !void { 138 const decl_index = mod.funcInfo(func_index).owner_decl; 139 // TODO: Separate types for generating decls and functions? 140 try self.genDecl(mod, decl_index, air, liveness); 141 } 142 143 pub fn updateDecl( 144 self: *Object, 145 mod: *Module, 146 decl_index: Decl.Index, 147 ) !void { 148 try self.genDecl(mod, decl_index, undefined, undefined); 149 } 150 151 /// Fetch or allocate a result id for decl index. This function also marks the decl as alive. 152 /// Note: Function does not actually generate the decl, it just allocates an index. 153 pub fn resolveDecl(self: *Object, mod: *Module, decl_index: Decl.Index) !SpvModule.Decl.Index { 154 const decl = mod.declPtr(decl_index); 155 try mod.markDeclAlive(decl); 156 157 const entry = try self.decl_link.getOrPut(self.gpa, decl_index); 158 if (!entry.found_existing) { 159 // TODO: Extern fn? 160 const kind: SpvModule.DeclKind = if (decl.val.isFuncBody(mod)) 161 .func 162 else 163 .global; 164 165 entry.value_ptr.* = try self.spv.allocDecl(kind); 166 } 167 168 return entry.value_ptr.*; 169 } 170 }; 171 172 /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that. 173 const DeclGen = struct { 174 /// A general-purpose allocator that can be used for any allocations for this DeclGen. 175 gpa: Allocator, 176 177 /// The object that this decl is generated into. 178 object: *Object, 179 180 /// The Zig module that we are generating decls for. 181 module: *Module, 182 183 /// The SPIR-V module that instructions should be emitted into. 184 /// This is the same as `self.object.spv`, repeated here for brevity. 185 spv: *SpvModule, 186 187 /// The decl we are currently generating code for. 188 decl_index: Decl.Index, 189 190 /// The intermediate code of the declaration we are currently generating. Note: If 191 /// the declaration is not a function, this value will be undefined! 192 air: Air, 193 194 /// The liveness analysis of the intermediate code for the declaration we are currently generating. 195 /// Note: If the declaration is not a function, this value will be undefined! 196 liveness: Liveness, 197 198 /// An array of function argument result-ids. Each index corresponds with the 199 /// function argument of the same index. 200 args: std.ArrayListUnmanaged(IdRef) = .{}, 201 202 /// A counter to keep track of how many `arg` instructions we've seen yet. 203 next_arg_index: u32 = 0, 204 205 /// A map keeping track of which instruction generated which result-id. 206 inst_results: InstMap = .{}, 207 208 /// A map that maps AIR intern pool indices to SPIR-V cache references. 209 /// See Object.type_map 210 type_map: *TypeMap, 211 212 /// We need to keep track of result ids for block labels, as well as the 'incoming' 213 /// blocks for a block. 214 blocks: BlockMap = .{}, 215 216 /// The label of the SPIR-V block we are currently generating. 217 current_block_label_id: IdRef, 218 219 /// The code (prologue and body) for the function we are currently generating code for. 220 func: SpvModule.Fn = .{}, 221 222 /// Stack of the base offsets of the current decl, which is what `dbg_stmt` is relative to. 223 /// This is a stack to keep track of inline functions. 224 base_line_stack: std.ArrayListUnmanaged(u32) = .{}, 225 226 /// If `gen` returned `Error.CodegenFail`, this contains an explanatory message. 227 /// Memory is owned by `module.gpa`. 228 error_msg: ?*Module.ErrorMsg = null, 229 230 /// Possible errors the `genDecl` function may return. 231 const Error = error{ CodegenFail, OutOfMemory }; 232 233 /// This structure is used to return information about a type typically used for 234 /// arithmetic operations. These types may either be integers, floats, or a vector 235 /// of these. Most scalar operations also work on vectors, so we can easily represent 236 /// those as arithmetic types. If the type is a scalar, 'inner type' refers to the 237 /// scalar type. Otherwise, if its a vector, it refers to the vector's element type. 238 const ArithmeticTypeInfo = struct { 239 /// A classification of the inner type. 240 const Class = enum { 241 /// A boolean. 242 bool, 243 244 /// A regular, **native**, integer. 245 /// This is only returned when the backend supports this int as a native type (when 246 /// the relevant capability is enabled). 247 integer, 248 249 /// A regular float. These are all required to be natively supported. Floating points 250 /// for which the relevant capability is not enabled are not emulated. 251 float, 252 253 /// An integer of a 'strange' size (which' bit size is not the same as its backing 254 /// type. **Note**: this may **also** include power-of-2 integers for which the 255 /// relevant capability is not enabled), but still within the limits of the largest 256 /// natively supported integer type. 257 strange_integer, 258 259 /// An integer with more bits than the largest natively supported integer type. 260 composite_integer, 261 }; 262 263 /// The number of bits in the inner type. 264 /// This is the actual number of bits of the type, not the size of the backing integer. 265 bits: u16, 266 267 /// The number of bits required to store the type. 268 /// For `integer` and `float`, this is equal to `bits`. 269 /// For `strange_integer` and `bool` this is the size of the backing integer. 270 /// For `composite_integer` this is 0 (TODO) 271 backing_bits: u16, 272 273 /// Whether the type is a vector. 274 is_vector: bool, 275 276 /// Whether the inner type is signed. Only relevant for integers. 277 signedness: std.builtin.Signedness, 278 279 /// A classification of the inner type. These scenarios 280 /// will all have to be handled slightly different. 281 class: Class, 282 }; 283 284 /// Data can be lowered into in two basic representations: indirect, which is when 285 /// a type is stored in memory, and direct, which is how a type is stored when its 286 /// a direct SPIR-V value. 287 const Repr = enum { 288 /// A SPIR-V value as it would be used in operations. 289 direct, 290 /// A SPIR-V value as it is stored in memory. 291 indirect, 292 }; 293 294 /// Free resources owned by the DeclGen. 295 pub fn deinit(self: *DeclGen) void { 296 self.args.deinit(self.gpa); 297 self.inst_results.deinit(self.gpa); 298 self.blocks.deinit(self.gpa); 299 self.func.deinit(self.gpa); 300 self.base_line_stack.deinit(self.gpa); 301 } 302 303 /// Return the target which we are currently compiling for. 304 pub fn getTarget(self: *DeclGen) std.Target { 305 return self.module.getTarget(); 306 } 307 308 pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error { 309 @setCold(true); 310 const mod = self.module; 311 const src = LazySrcLoc.nodeOffset(0); 312 const src_loc = src.toSrcLoc(self.module.declPtr(self.decl_index), mod); 313 assert(self.error_msg == null); 314 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); 315 return error.CodegenFail; 316 } 317 318 pub fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) Error { 319 return self.fail("TODO (SPIR-V): " ++ format, args); 320 } 321 322 /// Fetch the result-id for a previously generated instruction or constant. 323 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !IdRef { 324 const mod = self.module; 325 if (try self.air.value(inst, mod)) |val| { 326 const ty = self.typeOf(inst); 327 if (ty.zigTypeTag(mod) == .Fn) { 328 const fn_decl_index = switch (mod.intern_pool.indexToKey(val.ip_index)) { 329 .extern_func => |extern_func| extern_func.decl, 330 .func => |func| func.owner_decl, 331 else => unreachable, 332 }; 333 const spv_decl_index = try self.object.resolveDecl(mod, fn_decl_index); 334 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); 335 return self.spv.declPtr(spv_decl_index).result_id; 336 } 337 338 return try self.constant(ty, val, .direct); 339 } 340 const index = Air.refToIndex(inst).?; 341 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage. 342 } 343 344 fn resolveAnonDecl(self: *DeclGen, val: InternPool.Index, storage_class: StorageClass) !IdRef { 345 // TODO: This cannot be a function at this point, but it should probably be handled anyway. 346 const spv_decl_index = blk: { 347 const entry = try self.object.anon_decl_link.getOrPut(self.object.gpa, .{ val, storage_class }); 348 if (entry.found_existing) { 349 try self.func.decl_deps.put(self.spv.gpa, entry.value_ptr.*, {}); 350 return self.spv.declPtr(entry.value_ptr.*).result_id; 351 } 352 353 const spv_decl_index = try self.spv.allocDecl(.global); 354 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); 355 entry.value_ptr.* = spv_decl_index; 356 break :blk spv_decl_index; 357 }; 358 359 const mod = self.module; 360 const ty = mod.intern_pool.typeOf(val).toType(); 361 const ty_ref = try self.resolveType(ty, .indirect); 362 const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class); 363 364 const var_id = self.spv.declPtr(spv_decl_index).result_id; 365 366 const section = &self.spv.sections.types_globals_constants; 367 try section.emit(self.spv.gpa, .OpVariable, .{ 368 .id_result_type = self.typeId(ptr_ty_ref), 369 .id_result = var_id, 370 .storage_class = storage_class, 371 }); 372 373 // TODO: At some point we will be able to generate this all constant here, but then all of 374 // constant() will need to be implemented such that it doesn't generate any at-runtime code. 375 // NOTE: Because this is a global, we really only want to initialize it once. Therefore the 376 // constant lowering of this value will need to be deferred to some other function, which 377 // is then added to the list of initializers using endGlobal(). 378 379 // Save the current state so that we can temporarily generate into a different function. 380 // TODO: This should probably be made a little more robust. 381 const func = self.func; 382 defer self.func = func; 383 const block_label_id = self.current_block_label_id; 384 defer self.current_block_label_id = block_label_id; 385 386 self.func = .{}; 387 388 // TODO: Merge this with genDecl? 389 const begin = self.spv.beginGlobal(); 390 391 const void_ty_ref = try self.resolveType(Type.void, .direct); 392 const initializer_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{ 393 .return_type = void_ty_ref, 394 .parameters = &.{}, 395 } }); 396 397 const initializer_id = self.spv.allocId(); 398 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ 399 .id_result_type = self.typeId(void_ty_ref), 400 .id_result = initializer_id, 401 .function_control = .{}, 402 .function_type = self.typeId(initializer_proto_ty_ref), 403 }); 404 const root_block_id = self.spv.allocId(); 405 try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{ 406 .id_result = root_block_id, 407 }); 408 self.current_block_label_id = root_block_id; 409 410 const val_id = try self.constant(ty, val.toValue(), .indirect); 411 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 412 .pointer = var_id, 413 .object = val_id, 414 }); 415 416 self.spv.endGlobal(spv_decl_index, begin, var_id, initializer_id); 417 try self.func.body.emit(self.spv.gpa, .OpReturn, {}); 418 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); 419 try self.spv.addFunction(spv_decl_index, self.func); 420 421 try self.spv.debugNameFmt(var_id, "__anon_{d}", .{@intFromEnum(val)}); 422 try self.spv.debugNameFmt(initializer_id, "initializer of __anon_{d}", .{@intFromEnum(val)}); 423 424 return var_id; 425 } 426 427 /// Start a new SPIR-V block, Emits the label of the new block, and stores which 428 /// block we are currently generating. 429 /// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to 430 /// keep track of the previous block. 431 fn beginSpvBlock(self: *DeclGen, label_id: IdResult) !void { 432 try self.func.body.emit(self.spv.gpa, .OpLabel, .{ .id_result = label_id }); 433 self.current_block_label_id = label_id; 434 } 435 436 /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need 437 /// to emulate them in other instructions/types. This function returns, given an integer bit width (signed or unsigned, sign 438 /// included), the width of the underlying type which represents it, given the enabled features for the current target. 439 /// If the result is `null`, the largest type the target platform supports natively is not able to perform computations using 440 /// that size. In this case, multiple elements of the largest type should be used. 441 /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits. 442 /// The result is valid to be used with OpTypeInt. 443 /// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits). 444 /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers). 445 /// TODO: Should the result of this function be cached? 446 fn backingIntBits(self: *DeclGen, bits: u16) ?u16 { 447 const target = self.getTarget(); 448 449 // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function. 450 assert(bits != 0); 451 452 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively. 453 // 32-bit integers are always supported (see spec, 2.16.1, Data rules). 454 const ints = [_]struct { bits: u16, feature: ?Target.spirv.Feature }{ 455 .{ .bits = 8, .feature = .Int8 }, 456 .{ .bits = 16, .feature = .Int16 }, 457 .{ .bits = 32, .feature = null }, 458 .{ .bits = 64, .feature = .Int64 }, 459 }; 460 461 for (ints) |int| { 462 const has_feature = if (int.feature) |feature| 463 Target.spirv.featureSetHas(target.cpu.features, feature) 464 else 465 true; 466 467 if (bits <= int.bits and has_feature) { 468 return int.bits; 469 } 470 } 471 472 return null; 473 } 474 475 /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if 476 /// the Int64 capability is enabled). 477 /// Note: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits). 478 /// In theory that could also be used, but since the spec says that it only guarantees support up to 32-bit ints there 479 /// is no way of knowing whether those are actually supported. 480 /// TODO: Maybe this should be cached? 481 fn largestSupportedIntBits(self: *DeclGen) u16 { 482 const target = self.getTarget(); 483 return if (Target.spirv.featureSetHas(target.cpu.features, .Int64)) 484 64 485 else 486 32; 487 } 488 489 /// Checks whether the type is "composite int", an integer consisting of multiple native integers. These are represented by 490 /// arrays of largestSupportedIntBits(). 491 /// Asserts `ty` is an integer. 492 fn isCompositeInt(self: *DeclGen, ty: Type) bool { 493 return self.backingIntBits(ty) == null; 494 } 495 496 fn arithmeticTypeInfo(self: *DeclGen, ty: Type) !ArithmeticTypeInfo { 497 const mod = self.module; 498 const target = self.getTarget(); 499 return switch (ty.zigTypeTag(mod)) { 500 .Bool => ArithmeticTypeInfo{ 501 .bits = 1, // Doesn't matter for this class. 502 .backing_bits = self.backingIntBits(1).?, 503 .is_vector = false, 504 .signedness = .unsigned, // Technically, but doesn't matter for this class. 505 .class = .bool, 506 }, 507 .Float => ArithmeticTypeInfo{ 508 .bits = ty.floatBits(target), 509 .backing_bits = ty.floatBits(target), // TODO: F80? 510 .is_vector = false, 511 .signedness = .signed, // Technically, but doesn't matter for this class. 512 .class = .float, 513 }, 514 .Int => blk: { 515 const int_info = ty.intInfo(mod); 516 // TODO: Maybe it's useful to also return this value. 517 const maybe_backing_bits = self.backingIntBits(int_info.bits); 518 break :blk ArithmeticTypeInfo{ 519 .bits = int_info.bits, 520 .backing_bits = maybe_backing_bits orelse 0, 521 .is_vector = false, 522 .signedness = int_info.signedness, 523 .class = if (maybe_backing_bits) |backing_bits| 524 if (backing_bits == int_info.bits) 525 ArithmeticTypeInfo.Class.integer 526 else 527 ArithmeticTypeInfo.Class.strange_integer 528 else 529 .composite_integer, 530 }; 531 }, 532 .Enum => return self.arithmeticTypeInfo(ty.intTagType(mod)), 533 // As of yet, there is no vector support in the self-hosted compiler. 534 .Vector => blk: { 535 const child_type = ty.childType(mod); 536 const child_ty_info = try self.arithmeticTypeInfo(child_type); 537 break :blk ArithmeticTypeInfo{ 538 .bits = child_ty_info.bits, 539 .backing_bits = child_ty_info.backing_bits, 540 .is_vector = true, 541 .signedness = child_ty_info.signedness, 542 .class = child_ty_info.class, 543 }; 544 }, 545 // TODO: For which types is this the case? 546 // else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmt(self.module)}), 547 else => unreachable, 548 }; 549 } 550 551 /// Emits a bool constant in a particular representation. 552 fn constBool(self: *DeclGen, value: bool, repr: Repr) !IdRef { 553 switch (repr) { 554 .indirect => { 555 const int_ty_ref = try self.intType(.unsigned, 1); 556 return self.constInt(int_ty_ref, @intFromBool(value)); 557 }, 558 .direct => { 559 const bool_ty_ref = try self.resolveType(Type.bool, .direct); 560 return self.spv.constBool(bool_ty_ref, value); 561 }, 562 } 563 } 564 565 /// Emits an integer constant. 566 /// This function, unlike SpvModule.constInt, takes care to bitcast 567 /// the value to an unsigned int first for Kernels. 568 fn constInt(self: *DeclGen, ty_ref: CacheRef, value: anytype) !IdRef { 569 if (value < 0) { 570 const ty = self.spv.cache.lookup(ty_ref).int_type; 571 // Manually truncate the value so that the resulting value 572 // fits within the unsigned type. 573 const bits: u64 = @bitCast(@as(i64, @intCast(value))); 574 const truncated_bits = if (ty.bits == 64) 575 bits 576 else 577 bits & (@as(u64, 1) << @intCast(ty.bits)) - 1; 578 return try self.spv.constInt(ty_ref, truncated_bits); 579 } else { 580 return try self.spv.constInt(ty_ref, value); 581 } 582 } 583 584 /// Construct a struct at runtime. 585 /// result_ty_ref must be a struct type. 586 /// Constituents should be in `indirect` representation (as the elements of a struct should be). 587 /// Result is in `direct` representation. 588 fn constructStruct(self: *DeclGen, result_ty_ref: CacheRef, constituents: []const IdRef) !IdRef { 589 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' 590 // operands are not constant. 591 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 592 // For now, just initialize the struct by setting the fields manually... 593 // TODO: Make this OpCompositeConstruct when we can 594 const ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function); 595 const ptr_composite_id = self.spv.allocId(); 596 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ 597 .id_result_type = self.typeId(ptr_ty_ref), 598 .id_result = ptr_composite_id, 599 .storage_class = .Function, 600 }); 601 602 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).struct_type; 603 const member_types = spv_composite_ty.member_types; 604 605 for (constituents, member_types, 0..) |constitent_id, member_ty_ref, index| { 606 const ptr_member_ty_ref = try self.spv.ptrType(member_ty_ref, .Function); 607 const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))}); 608 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 609 .pointer = ptr_id, 610 .object = constitent_id, 611 }); 612 } 613 const result_id = self.spv.allocId(); 614 try self.func.body.emit(self.spv.gpa, .OpLoad, .{ 615 .id_result_type = self.typeId(result_ty_ref), 616 .id_result = result_id, 617 .pointer = ptr_composite_id, 618 }); 619 return result_id; 620 } 621 622 /// Construct an array at runtime. 623 /// result_ty_ref must be an array type. 624 /// Constituents should be in `indirect` representation (as the elements of an array should be). 625 /// Result is in `direct` representation. 626 fn constructArray(self: *DeclGen, result_ty_ref: CacheRef, constituents: []const IdRef) !IdRef { 627 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' 628 // operands are not constant. 629 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 630 // For now, just initialize the struct by setting the fields manually... 631 // TODO: Make this OpCompositeConstruct when we can 632 // TODO: Make this Function storage type 633 const ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function); 634 const ptr_composite_id = self.spv.allocId(); 635 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ 636 .id_result_type = self.typeId(ptr_ty_ref), 637 .id_result = ptr_composite_id, 638 .storage_class = .Function, 639 }); 640 641 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).array_type; 642 const elem_ty_ref = spv_composite_ty.element_type; 643 const ptr_elem_ty_ref = try self.spv.ptrType(elem_ty_ref, .Function); 644 645 for (constituents, 0..) |constitent_id, index| { 646 const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))}); 647 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 648 .pointer = ptr_id, 649 .object = constitent_id, 650 }); 651 } 652 const result_id = self.spv.allocId(); 653 try self.func.body.emit(self.spv.gpa, .OpLoad, .{ 654 .id_result_type = self.typeId(result_ty_ref), 655 .id_result = result_id, 656 .pointer = ptr_composite_id, 657 }); 658 return result_id; 659 } 660 661 /// This function generates a load for a constant in direct (ie, non-memory) representation. 662 /// When the constant is simple, it can be generated directly using OpConstant instructions. 663 /// When the constant is more complicated however, it needs to be constructed using multiple values. This 664 /// is done by emitting a sequence of instructions that initialize the value. 665 // 666 /// This function should only be called during function code generation. 667 fn constant(self: *DeclGen, ty: Type, arg_val: Value, repr: Repr) !IdRef { 668 const mod = self.module; 669 const target = self.getTarget(); 670 const result_ty_ref = try self.resolveType(ty, repr); 671 const ip = &mod.intern_pool; 672 673 var val = arg_val; 674 switch (ip.indexToKey(val.toIntern())) { 675 .runtime_value => |rt| val = rt.val.toValue(), 676 else => {}, 677 } 678 679 log.debug("constant: ty = {}, val = {}", .{ ty.fmt(mod), val.fmtValue(ty, mod) }); 680 if (val.isUndefDeep(mod)) { 681 return self.spv.constUndef(result_ty_ref); 682 } 683 684 switch (ip.indexToKey(val.toIntern())) { 685 .int_type, 686 .ptr_type, 687 .array_type, 688 .vector_type, 689 .opt_type, 690 .anyframe_type, 691 .error_union_type, 692 .simple_type, 693 .struct_type, 694 .anon_struct_type, 695 .union_type, 696 .opaque_type, 697 .enum_type, 698 .func_type, 699 .error_set_type, 700 .inferred_error_set_type, 701 => unreachable, // types, not values 702 703 .undef, .runtime_value => unreachable, // handled above 704 705 .variable, 706 .extern_func, 707 .func, 708 .enum_literal, 709 .empty_enum_value, 710 => unreachable, // non-runtime values 711 712 .simple_value => |simple_value| switch (simple_value) { 713 .undefined, 714 .void, 715 .null, 716 .empty_struct, 717 .@"unreachable", 718 .generic_poison, 719 => unreachable, // non-runtime values 720 721 .false, .true => return try self.constBool(val.toBool(), repr), 722 }, 723 724 .int => { 725 if (ty.isSignedInt(mod)) { 726 return try self.constInt(result_ty_ref, val.toSignedInt(mod)); 727 } else { 728 return try self.constInt(result_ty_ref, val.toUnsignedInt(mod)); 729 } 730 }, 731 .float => return switch (ty.floatBits(target)) { 732 16 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float16 = val.toFloat(f16, mod) } } }), 733 32 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float32 = val.toFloat(f32, mod) } } }), 734 64 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float64 = val.toFloat(f64, mod) } } }), 735 80, 128 => unreachable, // TODO 736 else => unreachable, 737 }, 738 .err => |err| { 739 const value = try mod.getErrorValue(err.name); 740 return try self.constInt(result_ty_ref, value); 741 }, 742 .error_union => |error_union| { 743 // TODO: Error unions may be constructed with constant instructions if the payload type 744 // allows it. For now, just generate it here regardless. 745 const err_ty = switch (error_union.val) { 746 .err_name => ty.errorUnionSet(mod), 747 .payload => Type.err_int, 748 }; 749 const err_val = switch (error_union.val) { 750 .err_name => |err_name| (try mod.intern(.{ .err = .{ 751 .ty = ty.errorUnionSet(mod).toIntern(), 752 .name = err_name, 753 } })).toValue(), 754 .payload => try mod.intValue(Type.err_int, 0), 755 }; 756 const payload_ty = ty.errorUnionPayload(mod); 757 const eu_layout = self.errorUnionLayout(payload_ty); 758 if (!eu_layout.payload_has_bits) { 759 // We use the error type directly as the type. 760 return try self.constant(err_ty, err_val, .indirect); 761 } 762 763 const payload_val = switch (error_union.val) { 764 .err_name => try mod.intern(.{ .undef = payload_ty.toIntern() }), 765 .payload => |payload| payload, 766 }.toValue(); 767 768 var constituents: [2]IdRef = undefined; 769 if (eu_layout.error_first) { 770 constituents[0] = try self.constant(err_ty, err_val, .indirect); 771 constituents[1] = try self.constant(payload_ty, payload_val, .indirect); 772 } else { 773 constituents[0] = try self.constant(payload_ty, payload_val, .indirect); 774 constituents[1] = try self.constant(err_ty, err_val, .indirect); 775 } 776 777 return try self.constructStruct(result_ty_ref, &constituents); 778 }, 779 .enum_tag => { 780 const int_val = try val.intFromEnum(ty, mod); 781 const int_ty = ty.intTagType(mod); 782 return try self.constant(int_ty, int_val, repr); 783 }, 784 .ptr => |ptr| { 785 const ptr_ty = switch (ptr.len) { 786 .none => ty, 787 else => ty.slicePtrFieldType(mod), 788 }; 789 const ptr_id = try self.constantPtr(ptr_ty, val); 790 if (ptr.len == .none) { 791 return ptr_id; 792 } 793 794 const len_id = try self.constant(Type.usize, ptr.len.toValue(), .indirect); 795 return try self.constructStruct(result_ty_ref, &.{ ptr_id, len_id }); 796 }, 797 .opt => { 798 const payload_ty = ty.optionalChild(mod); 799 const maybe_payload_val = val.optionalValue(mod); 800 801 if (!payload_ty.hasRuntimeBits(mod)) { 802 return try self.constBool(maybe_payload_val != null, .indirect); 803 } else if (ty.optionalReprIsPayload(mod)) { 804 // Optional representation is a nullable pointer or slice. 805 if (maybe_payload_val) |payload_val| { 806 return try self.constant(payload_ty, payload_val, .indirect); 807 } else { 808 const ptr_ty_ref = try self.resolveType(ty, .indirect); 809 return self.spv.constNull(ptr_ty_ref); 810 } 811 } 812 813 // Optional representation is a structure. 814 // { Payload, Bool } 815 816 const has_pl_id = try self.constBool(maybe_payload_val != null, .indirect); 817 const payload_id = if (maybe_payload_val) |payload_val| 818 try self.constant(payload_ty, payload_val, .indirect) 819 else 820 try self.spv.constUndef(try self.resolveType(payload_ty, .indirect)); 821 822 return try self.constructStruct(result_ty_ref, &.{ payload_id, has_pl_id }); 823 }, 824 .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) { 825 inline .array_type, .vector_type => |array_type, tag| { 826 const elem_ty = array_type.child.toType(); 827 const elem_ty_ref = try self.resolveType(elem_ty, .indirect); 828 829 const constituents = try self.gpa.alloc(IdRef, @as(u32, @intCast(ty.arrayLenIncludingSentinel(mod)))); 830 defer self.gpa.free(constituents); 831 832 switch (aggregate.storage) { 833 .bytes => |bytes| { 834 // TODO: This is really space inefficient, perhaps there is a better 835 // way to do it? 836 for (bytes, 0..) |byte, i| { 837 constituents[i] = try self.constInt(elem_ty_ref, byte); 838 } 839 }, 840 .elems => |elems| { 841 for (0..@as(usize, @intCast(array_type.len))) |i| { 842 constituents[i] = try self.constant(elem_ty, elems[i].toValue(), .indirect); 843 } 844 }, 845 .repeated_elem => |elem| { 846 const val_id = try self.constant(elem_ty, elem.toValue(), .indirect); 847 for (0..@as(usize, @intCast(array_type.len))) |i| { 848 constituents[i] = val_id; 849 } 850 }, 851 } 852 853 switch (tag) { 854 inline .array_type => if (array_type.sentinel != .none) { 855 constituents[constituents.len - 1] = try self.constant(elem_ty, array_type.sentinel.toValue(), .indirect); 856 }, 857 else => {}, 858 } 859 860 return try self.constructArray(result_ty_ref, constituents); 861 }, 862 .struct_type => { 863 const struct_type = mod.typeToStruct(ty).?; 864 if (struct_type.layout == .Packed) { 865 return self.todo("packed struct constants", .{}); 866 } 867 868 var constituents = std.ArrayList(IdRef).init(self.gpa); 869 defer constituents.deinit(); 870 871 var it = struct_type.iterateRuntimeOrder(ip); 872 while (it.next()) |field_index| { 873 const field_ty = struct_type.field_types.get(ip)[field_index].toType(); 874 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { 875 // This is a zero-bit field - we only needed it for the alignment. 876 continue; 877 } 878 879 // TODO: Padding? 880 const field_val = try val.fieldValue(mod, field_index); 881 const field_id = try self.constant(field_ty, field_val, .indirect); 882 883 try constituents.append(field_id); 884 } 885 886 return try self.constructStruct(result_ty_ref, constituents.items); 887 }, 888 .anon_struct_type => unreachable, // TODO 889 else => unreachable, 890 }, 891 .un => |un| { 892 const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?; 893 const layout = self.unionLayout(ty, active_field); 894 const payload = if (layout.active_field_size != 0) 895 try self.constant(layout.active_field_ty, un.val.toValue(), .indirect) 896 else 897 null; 898 899 return try self.unionInit(ty, active_field, payload); 900 }, 901 .memoized_call => unreachable, 902 } 903 } 904 905 fn constantPtr(self: *DeclGen, ptr_ty: Type, ptr_val: Value) Error!IdRef { 906 const result_ty_ref = try self.resolveType(ptr_ty, .direct); 907 const mod = self.module; 908 switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) { 909 .decl => |decl| return try self.constantDeclRef(ptr_ty, decl), 910 .mut_decl => |decl_mut| return try self.constantDeclRef(ptr_ty, decl_mut.decl), 911 .anon_decl => |anon_decl| return try self.constantAnonDeclRef(ptr_ty, anon_decl), 912 .int => |int| { 913 const ptr_id = self.spv.allocId(); 914 // TODO: This can probably be an OpSpecConstantOp Bitcast, but 915 // that is not implemented by Mesa yet. Therefore, just generate it 916 // as a runtime operation. 917 try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{ 918 .id_result_type = self.typeId(result_ty_ref), 919 .id_result = ptr_id, 920 .integer_value = try self.constant(Type.usize, int.toValue(), .direct), 921 }); 922 return ptr_id; 923 }, 924 .eu_payload => unreachable, // TODO 925 .opt_payload => unreachable, // TODO 926 .comptime_field => unreachable, 927 .elem => |elem_ptr| { 928 const parent_ptr_ty = mod.intern_pool.typeOf(elem_ptr.base).toType(); 929 const parent_ptr_id = try self.constantPtr(parent_ptr_ty, elem_ptr.base.toValue()); 930 const size_ty_ref = try self.sizeType(); 931 const index_id = try self.constInt(size_ty_ref, elem_ptr.index); 932 933 const elem_ptr_id = try self.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id); 934 935 // TODO: Can we consolidate this in ptrElemPtr? 936 const elem_ty = parent_ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T. 937 const elem_ty_ref = try self.resolveType(elem_ty, .direct); 938 const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(parent_ptr_ty.ptrAddressSpace(mod))); 939 940 if (elem_ptr_ty_ref == result_ty_ref) { 941 return elem_ptr_id; 942 } 943 // This may happen when we have pointer-to-array and the result is 944 // another pointer-to-array instead of a pointer-to-element. 945 const result_id = self.spv.allocId(); 946 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 947 .id_result_type = self.typeId(result_ty_ref), 948 .id_result = result_id, 949 .operand = elem_ptr_id, 950 }); 951 return result_id; 952 }, 953 .field => |field| { 954 const base_ptr_ty = mod.intern_pool.typeOf(field.base).toType(); 955 const base_ptr = try self.constantPtr(base_ptr_ty, field.base.toValue()); 956 const field_index: u32 = @intCast(field.index); 957 return try self.structFieldPtr(ptr_ty, base_ptr_ty, base_ptr, field_index); 958 }, 959 } 960 } 961 962 fn constantAnonDeclRef(self: *DeclGen, ty: Type, decl_val: InternPool.Index) !IdRef { 963 // TODO: Merge this function with constantDeclRef. 964 965 const mod = self.module; 966 const ip = &mod.intern_pool; 967 const ty_ref = try self.resolveType(ty, .direct); 968 const decl_ty = ip.typeOf(decl_val).toType(); 969 970 if (decl_val.toValue().getFunction(mod)) |func| { 971 _ = func; 972 unreachable; // TODO 973 } else if (decl_val.toValue().getExternFunc(mod)) |func| { 974 _ = func; 975 unreachable; 976 } 977 978 // const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; 979 if (!decl_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { 980 // Pointer to nothing - return undefoined 981 return self.spv.constUndef(ty_ref); 982 } 983 984 if (decl_ty.zigTypeTag(mod) == .Fn) { 985 unreachable; // TODO 986 } 987 988 const final_storage_class = spvStorageClass(ty.ptrAddressSpace(mod)); 989 const actual_storage_class = switch (final_storage_class) { 990 .Generic => .CrossWorkgroup, 991 else => |other| other, 992 }; 993 994 const decl_id = try self.resolveAnonDecl(decl_val, actual_storage_class); 995 const decl_ty_ref = try self.resolveType(decl_ty, .indirect); 996 const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class); 997 998 const ptr_id = switch (final_storage_class) { 999 .Generic => blk: { 1000 const result_id = self.spv.allocId(); 1001 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ 1002 .id_result_type = self.typeId(decl_ptr_ty_ref), 1003 .id_result = result_id, 1004 .pointer = decl_id, 1005 }); 1006 break :blk result_id; 1007 }, 1008 else => decl_id, 1009 }; 1010 1011 if (decl_ptr_ty_ref != ty_ref) { 1012 // Differing pointer types, insert a cast. 1013 const casted_ptr_id = self.spv.allocId(); 1014 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 1015 .id_result_type = self.typeId(ty_ref), 1016 .id_result = casted_ptr_id, 1017 .operand = ptr_id, 1018 }); 1019 return casted_ptr_id; 1020 } else { 1021 return ptr_id; 1022 } 1023 } 1024 1025 fn constantDeclRef(self: *DeclGen, ty: Type, decl_index: Decl.Index) !IdRef { 1026 const mod = self.module; 1027 const ty_ref = try self.resolveType(ty, .direct); 1028 const ty_id = self.typeId(ty_ref); 1029 const decl = mod.declPtr(decl_index); 1030 switch (mod.intern_pool.indexToKey(decl.val.ip_index)) { 1031 .func => { 1032 // TODO: Properly lower function pointers. For now we are going to hack around it and 1033 // just generate an empty pointer. Function pointers are represented by a pointer to usize. 1034 return try self.spv.constUndef(ty_ref); 1035 }, 1036 .extern_func => unreachable, // TODO 1037 else => {}, 1038 } 1039 1040 if (!decl.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { 1041 // Pointer to nothing - return undefined. 1042 return self.spv.constUndef(ty_ref); 1043 } 1044 1045 const spv_decl_index = try self.object.resolveDecl(mod, decl_index); 1046 1047 const decl_id = self.spv.declPtr(spv_decl_index).result_id; 1048 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); 1049 1050 const final_storage_class = spvStorageClass(decl.@"addrspace"); 1051 1052 const decl_ty_ref = try self.resolveType(decl.ty, .indirect); 1053 const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class); 1054 1055 const ptr_id = switch (final_storage_class) { 1056 .Generic => blk: { 1057 // Pointer should be Generic, but is actually placed in CrossWorkgroup. 1058 const result_id = self.spv.allocId(); 1059 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ 1060 .id_result_type = self.typeId(decl_ptr_ty_ref), 1061 .id_result = result_id, 1062 .pointer = decl_id, 1063 }); 1064 break :blk result_id; 1065 }, 1066 else => decl_id, 1067 }; 1068 1069 if (decl_ptr_ty_ref != ty_ref) { 1070 // Differing pointer types, insert a cast. 1071 const casted_ptr_id = self.spv.allocId(); 1072 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 1073 .id_result_type = ty_id, 1074 .id_result = casted_ptr_id, 1075 .operand = ptr_id, 1076 }); 1077 return casted_ptr_id; 1078 } else { 1079 return ptr_id; 1080 } 1081 } 1082 1083 // Turn a Zig type's name into a cache reference. 1084 fn resolveTypeName(self: *DeclGen, ty: Type) !CacheString { 1085 var name = std.ArrayList(u8).init(self.gpa); 1086 defer name.deinit(); 1087 try ty.print(name.writer(), self.module); 1088 return try self.spv.resolveString(name.items); 1089 } 1090 1091 /// Turn a Zig type into a SPIR-V Type, and return its type result-id. 1092 fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType { 1093 const type_ref = try self.resolveType(ty, .direct); 1094 return self.spv.resultId(type_ref); 1095 } 1096 1097 fn typeId(self: *DeclGen, ty_ref: CacheRef) IdRef { 1098 return self.spv.resultId(ty_ref); 1099 } 1100 1101 /// Create an integer type suitable for storing at least 'bits' bits. 1102 /// The integer type that is returned by this function is the type that is used to perform 1103 /// actual operations (as well as store) a Zig type of a particular number of bits. To create 1104 /// a type with an exact size, use SpvModule.intType. 1105 fn intType(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !CacheRef { 1106 const backing_bits = self.backingIntBits(bits) orelse { 1107 // TODO: Integers too big for any native type are represented as "composite integers": 1108 // An array of largestSupportedIntBits. 1109 return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits }); 1110 }; 1111 // Kernel only supports unsigned ints. 1112 // TODO: Only do this with Kernels 1113 return self.spv.intType(.unsigned, backing_bits); 1114 } 1115 1116 /// Create an integer type that represents 'usize'. 1117 fn sizeType(self: *DeclGen) !CacheRef { 1118 return try self.intType(.unsigned, self.getTarget().ptrBitWidth()); 1119 } 1120 1121 /// Generate a union type, optionally with a known field. If the tag alignment is greater 1122 /// than that of the payload, a regular union (non-packed, with both tag and payload), will 1123 /// be generated as follows: 1124 /// If the active field is known: 1125 /// struct { 1126 /// tag: TagType, 1127 /// payload: ActivePayloadType, 1128 /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8, 1129 /// padding: [padding_size]u8, 1130 /// } 1131 /// If the payload alignment is greater than that of the tag: 1132 /// struct { 1133 /// payload: ActivePayloadType, 1134 /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8, 1135 /// tag: TagType, 1136 /// padding: [padding_size]u8, 1137 /// } 1138 /// If the active payload is unknown, it will default back to the most aligned field. This is 1139 /// to make sure that the overal struct has the correct alignment in spir-v. 1140 /// If any of the fields' size is 0, it will be omitted. 1141 /// NOTE: When the active field is set to something other than the most aligned field, the 1142 /// resulting struct will be *underaligned*. 1143 fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef { 1144 const mod = self.module; 1145 const ip = &mod.intern_pool; 1146 const union_obj = mod.typeToUnion(ty).?; 1147 1148 if (union_obj.getLayout(ip) == .Packed) { 1149 return self.todo("packed union types", .{}); 1150 } 1151 1152 const layout = self.unionLayout(ty, maybe_active_field); 1153 1154 if (layout.payload_size == 0) { 1155 // No payload, so represent this as just the tag type. 1156 return try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect); 1157 } 1158 1159 // TODO: We need to add the active field to the key, somehow. 1160 if (maybe_active_field == null) { 1161 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref; 1162 } 1163 1164 var member_types: [4]CacheRef = undefined; 1165 var member_names: [4]CacheString = undefined; 1166 1167 const u8_ty_ref = try self.intType(.unsigned, 8); // TODO: What if Int8Type is not enabled? 1168 1169 if (layout.tag_size != 0) { 1170 const tag_ty_ref = try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect); 1171 member_types[layout.tag_index] = tag_ty_ref; 1172 member_names[layout.tag_index] = try self.spv.resolveString("(tag)"); 1173 } 1174 1175 if (layout.active_field_size != 0) { 1176 const active_payload_ty_ref = try self.resolveType(layout.active_field_ty, .indirect); 1177 member_types[layout.active_field_index] = active_payload_ty_ref; 1178 member_names[layout.active_field_index] = try self.spv.resolveString("(payload)"); 1179 } 1180 1181 if (layout.payload_padding_size != 0) { 1182 const payload_padding_ty_ref = try self.spv.arrayType(@intCast(layout.payload_padding_size), u8_ty_ref); 1183 member_types[layout.payload_padding_index] = payload_padding_ty_ref; 1184 member_names[layout.payload_padding_index] = try self.spv.resolveString("(payload padding)"); 1185 } 1186 1187 if (layout.padding_size != 0) { 1188 const padding_ty_ref = try self.spv.arrayType(@intCast(layout.padding_size), u8_ty_ref); 1189 member_types[layout.padding_index] = padding_ty_ref; 1190 member_names[layout.padding_index] = try self.spv.resolveString("(padding)"); 1191 } 1192 1193 const ty_ref = try self.spv.resolve(.{ .struct_type = .{ 1194 .name = try self.resolveTypeName(ty), 1195 .member_types = member_types[0..layout.total_fields], 1196 .member_names = member_names[0..layout.total_fields], 1197 } }); 1198 1199 if (maybe_active_field == null) { 1200 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); 1201 } 1202 return ty_ref; 1203 } 1204 1205 fn resolveFnReturnType(self: *DeclGen, ret_ty: Type) !CacheRef { 1206 const mod = self.module; 1207 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) { 1208 // If the return type is an error set or an error union, then we make this 1209 // anyerror return type instead, so that it can be coerced into a function 1210 // pointer type which has anyerror as the return type. 1211 if (ret_ty.isError(mod)) { 1212 return self.resolveType(Type.anyerror, .direct); 1213 } else { 1214 return self.resolveType(Type.void, .direct); 1215 } 1216 } 1217 1218 return try self.resolveType(ret_ty, .direct); 1219 } 1220 1221 /// Turn a Zig type into a SPIR-V Type, and return a reference to it. 1222 fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!CacheRef { 1223 const mod = self.module; 1224 const ip = &mod.intern_pool; 1225 log.debug("resolveType: ty = {}", .{ty.fmt(mod)}); 1226 const target = self.getTarget(); 1227 switch (ty.zigTypeTag(mod)) { 1228 .NoReturn => { 1229 assert(repr == .direct); 1230 return try self.spv.resolve(.void_type); 1231 }, 1232 .Void => switch (repr) { 1233 .direct => return try self.spv.resolve(.void_type), 1234 // Pointers to void 1235 .indirect => return try self.spv.resolve(.{ .opaque_type = .{ 1236 .name = try self.spv.resolveString("void"), 1237 } }), 1238 }, 1239 .Bool => switch (repr) { 1240 .direct => return try self.spv.resolve(.bool_type), 1241 .indirect => return try self.intType(.unsigned, 1), 1242 }, 1243 .Int => { 1244 const int_info = ty.intInfo(mod); 1245 if (int_info.bits == 0) { 1246 // Some times, the backend will be asked to generate a pointer to i0. OpTypeInt 1247 // with 0 bits is invalid, so return an opaque type in this case. 1248 assert(repr == .indirect); 1249 return try self.spv.resolve(.{ .opaque_type = .{ 1250 .name = try self.spv.resolveString("u0"), 1251 } }); 1252 } 1253 return try self.intType(int_info.signedness, int_info.bits); 1254 }, 1255 .Enum => { 1256 const tag_ty = ty.intTagType(mod); 1257 return self.resolveType(tag_ty, repr); 1258 }, 1259 .Float => { 1260 // We can (and want) not really emulate floating points with other floating point types like with the integer types, 1261 // so if the float is not supported, just return an error. 1262 const bits = ty.floatBits(target); 1263 const supported = switch (bits) { 1264 16 => Target.spirv.featureSetHas(target.cpu.features, .Float16), 1265 // 32-bit floats are always supported (see spec, 2.16.1, Data rules). 1266 32 => true, 1267 64 => Target.spirv.featureSetHas(target.cpu.features, .Float64), 1268 else => false, 1269 }; 1270 1271 if (!supported) { 1272 return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits}); 1273 } 1274 1275 return try self.spv.resolve(.{ .float_type = .{ .bits = bits } }); 1276 }, 1277 .Array => { 1278 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref; 1279 1280 const elem_ty = ty.childType(mod); 1281 const elem_ty_ref = try self.resolveType(elem_ty, .indirect); 1282 var total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(mod)) orelse { 1283 return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(mod)}); 1284 }; 1285 const ty_ref = if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) blk: { 1286 // The size of the array would be 0, but that is not allowed in SPIR-V. 1287 // This path can be reached when the backend is asked to generate a pointer to 1288 // an array of some zero-bit type. This should always be an indirect path. 1289 assert(repr == .indirect); 1290 1291 // We cannot use the child type here, so just use an opaque type. 1292 break :blk try self.spv.resolve(.{ .opaque_type = .{ 1293 .name = try self.spv.resolveString("zero-sized array"), 1294 } }); 1295 } else if (total_len == 0) blk: { 1296 // The size of the array would be 0, but that is not allowed in SPIR-V. 1297 // This path can be reached for example when there is a slicing of a pointer 1298 // that produces a zero-length array. In all cases where this type can be generated, 1299 // this should be an indirect path. 1300 assert(repr == .indirect); 1301 1302 // In this case, we have an array of a non-zero sized type. In this case, 1303 // generate an array of 1 element instead, so that ptr_elem_ptr instructions 1304 // can be lowered to ptrAccessChain instead of manually performing the math. 1305 break :blk try self.spv.arrayType(1, elem_ty_ref); 1306 } else try self.spv.arrayType(total_len, elem_ty_ref); 1307 1308 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); 1309 return ty_ref; 1310 }, 1311 .Fn => switch (repr) { 1312 .direct => { 1313 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref; 1314 1315 const fn_info = mod.typeToFunc(ty).?; 1316 // TODO: Put this somewhere in Sema.zig 1317 if (fn_info.is_var_args) 1318 return self.fail("VarArgs functions are unsupported for SPIR-V", .{}); 1319 1320 const param_ty_refs = try self.gpa.alloc(CacheRef, fn_info.param_types.len); 1321 defer self.gpa.free(param_ty_refs); 1322 var param_index: usize = 0; 1323 for (fn_info.param_types.get(ip)) |param_ty_index| { 1324 const param_ty = param_ty_index.toType(); 1325 if (!param_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; 1326 1327 param_ty_refs[param_index] = try self.resolveType(param_ty, .direct); 1328 param_index += 1; 1329 } 1330 const return_ty_ref = try self.resolveFnReturnType(fn_info.return_type.toType()); 1331 1332 const ty_ref = try self.spv.resolve(.{ .function_type = .{ 1333 .return_type = return_ty_ref, 1334 .parameters = param_ty_refs[0..param_index], 1335 } }); 1336 1337 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); 1338 return ty_ref; 1339 }, 1340 .indirect => { 1341 // TODO: Represent function pointers properly. 1342 // For now, just use an usize type. 1343 return try self.sizeType(); 1344 }, 1345 }, 1346 .Pointer => { 1347 const ptr_info = ty.ptrInfo(mod); 1348 1349 const storage_class = spvStorageClass(ptr_info.flags.address_space); 1350 const child_ty_ref = try self.resolveType(ptr_info.child.toType(), .indirect); 1351 const ptr_ty_ref = try self.spv.resolve(.{ .ptr_type = .{ 1352 .storage_class = storage_class, 1353 .child_type = child_ty_ref, 1354 } }); 1355 if (ptr_info.flags.size != .Slice) { 1356 return ptr_ty_ref; 1357 } 1358 1359 const size_ty_ref = try self.sizeType(); 1360 return self.spv.resolve(.{ .struct_type = .{ 1361 .member_types = &.{ ptr_ty_ref, size_ty_ref }, 1362 .member_names = &.{ 1363 try self.spv.resolveString("ptr"), 1364 try self.spv.resolveString("len"), 1365 }, 1366 } }); 1367 }, 1368 .Vector => { 1369 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref; 1370 1371 const elem_ty = ty.childType(mod); 1372 const elem_ty_ref = try self.resolveType(elem_ty, .indirect); 1373 1374 const ty_ref = try self.spv.arrayType(ty.vectorLen(mod), elem_ty_ref); 1375 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); 1376 return ty_ref; 1377 }, 1378 .Struct => { 1379 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref; 1380 1381 const struct_type = switch (ip.indexToKey(ty.toIntern())) { 1382 .anon_struct_type => |tuple| { 1383 const member_types = try self.gpa.alloc(CacheRef, tuple.values.len); 1384 defer self.gpa.free(member_types); 1385 1386 var member_index: usize = 0; 1387 for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| { 1388 if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue; 1389 1390 member_types[member_index] = try self.resolveType(field_ty.toType(), .indirect); 1391 member_index += 1; 1392 } 1393 1394 const ty_ref = try self.spv.resolve(.{ .struct_type = .{ 1395 .name = try self.resolveTypeName(ty), 1396 .member_types = member_types[0..member_index], 1397 } }); 1398 1399 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); 1400 return ty_ref; 1401 }, 1402 .struct_type => |struct_type| struct_type, 1403 else => unreachable, 1404 }; 1405 1406 if (struct_type.layout == .Packed) { 1407 return try self.resolveType(struct_type.backingIntType(ip).toType(), .direct); 1408 } 1409 1410 var member_types = std.ArrayList(CacheRef).init(self.gpa); 1411 defer member_types.deinit(); 1412 1413 var member_names = std.ArrayList(CacheString).init(self.gpa); 1414 defer member_names.deinit(); 1415 1416 var it = struct_type.iterateRuntimeOrder(ip); 1417 while (it.next()) |field_index| { 1418 const field_ty = struct_type.field_types.get(ip)[field_index].toType(); 1419 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { 1420 // This is a zero-bit field - we only needed it for the alignment. 1421 continue; 1422 } 1423 1424 const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse 1425 try ip.getOrPutStringFmt(mod.gpa, "{d}", .{field_index}); 1426 try member_types.append(try self.resolveType(field_ty, .indirect)); 1427 try member_names.append(try self.spv.resolveString(ip.stringToSlice(field_name))); 1428 } 1429 1430 const ty_ref = try self.spv.resolve(.{ .struct_type = .{ 1431 .name = try self.resolveTypeName(ty), 1432 .member_types = member_types.items, 1433 .member_names = member_names.items, 1434 } }); 1435 1436 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); 1437 return ty_ref; 1438 }, 1439 .Optional => { 1440 const payload_ty = ty.optionalChild(mod); 1441 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { 1442 // Just use a bool. 1443 // Note: Always generate the bool with indirect format, to save on some sanity 1444 // Perform the conversion to a direct bool when the field is extracted. 1445 return try self.resolveType(Type.bool, .indirect); 1446 } 1447 1448 const payload_ty_ref = try self.resolveType(payload_ty, .indirect); 1449 if (ty.optionalReprIsPayload(mod)) { 1450 // Optional is actually a pointer or a slice. 1451 return payload_ty_ref; 1452 } 1453 1454 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref; 1455 1456 const bool_ty_ref = try self.resolveType(Type.bool, .indirect); 1457 1458 const ty_ref = try self.spv.resolve(.{ .struct_type = .{ 1459 .member_types = &.{ payload_ty_ref, bool_ty_ref }, 1460 .member_names = &.{ 1461 try self.spv.resolveString("payload"), 1462 try self.spv.resolveString("valid"), 1463 }, 1464 } }); 1465 1466 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); 1467 return ty_ref; 1468 }, 1469 .Union => return try self.resolveUnionType(ty, null), 1470 .ErrorSet => return try self.intType(.unsigned, 16), 1471 .ErrorUnion => { 1472 const payload_ty = ty.errorUnionPayload(mod); 1473 const error_ty_ref = try self.resolveType(Type.anyerror, .indirect); 1474 1475 const eu_layout = self.errorUnionLayout(payload_ty); 1476 if (!eu_layout.payload_has_bits) { 1477 return error_ty_ref; 1478 } 1479 1480 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref; 1481 1482 const payload_ty_ref = try self.resolveType(payload_ty, .indirect); 1483 1484 var member_types: [2]CacheRef = undefined; 1485 var member_names: [2]CacheString = undefined; 1486 if (eu_layout.error_first) { 1487 // Put the error first 1488 member_types = .{ error_ty_ref, payload_ty_ref }; 1489 member_names = .{ 1490 try self.spv.resolveString("error"), 1491 try self.spv.resolveString("payload"), 1492 }; 1493 // TODO: ABI padding? 1494 } else { 1495 // Put the payload first. 1496 member_types = .{ payload_ty_ref, error_ty_ref }; 1497 member_names = .{ 1498 try self.spv.resolveString("payload"), 1499 try self.spv.resolveString("error"), 1500 }; 1501 // TODO: ABI padding? 1502 } 1503 1504 const ty_ref = try self.spv.resolve(.{ .struct_type = .{ 1505 .name = try self.resolveTypeName(ty), 1506 .member_types = &member_types, 1507 .member_names = &member_names, 1508 } }); 1509 1510 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); 1511 return ty_ref; 1512 }, 1513 .Opaque => { 1514 return try self.spv.resolve(.{ 1515 .opaque_type = .{ 1516 .name = .none, // TODO 1517 }, 1518 }); 1519 }, 1520 1521 .Null, 1522 .Undefined, 1523 .EnumLiteral, 1524 .ComptimeFloat, 1525 .ComptimeInt, 1526 .Type, 1527 => unreachable, // Must be comptime. 1528 1529 else => |tag| return self.todo("Implement zig type '{}'", .{tag}), 1530 } 1531 } 1532 1533 fn spvStorageClass(as: std.builtin.AddressSpace) StorageClass { 1534 return switch (as) { 1535 .generic => .Generic, 1536 .shared => .Workgroup, 1537 .local => .Private, 1538 .global => .CrossWorkgroup, 1539 .constant => .UniformConstant, 1540 .gs, 1541 .fs, 1542 .ss, 1543 .param, 1544 .flash, 1545 .flash1, 1546 .flash2, 1547 .flash3, 1548 .flash4, 1549 .flash5, 1550 => unreachable, 1551 }; 1552 } 1553 1554 const ErrorUnionLayout = struct { 1555 payload_has_bits: bool, 1556 error_first: bool, 1557 1558 fn errorFieldIndex(self: @This()) u32 { 1559 assert(self.payload_has_bits); 1560 return if (self.error_first) 0 else 1; 1561 } 1562 1563 fn payloadFieldIndex(self: @This()) u32 { 1564 assert(self.payload_has_bits); 1565 return if (self.error_first) 1 else 0; 1566 } 1567 }; 1568 1569 fn errorUnionLayout(self: *DeclGen, payload_ty: Type) ErrorUnionLayout { 1570 const mod = self.module; 1571 1572 const error_align = Type.anyerror.abiAlignment(mod); 1573 const payload_align = payload_ty.abiAlignment(mod); 1574 1575 const error_first = error_align.compare(.gt, payload_align); 1576 return .{ 1577 .payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(mod), 1578 .error_first = error_first, 1579 }; 1580 } 1581 1582 const UnionLayout = struct { 1583 active_field: u32, 1584 active_field_ty: Type, 1585 payload_size: u32, 1586 1587 tag_size: u32, 1588 tag_index: u32, 1589 active_field_size: u32, 1590 active_field_index: u32, 1591 payload_padding_size: u32, 1592 payload_padding_index: u32, 1593 padding_size: u32, 1594 padding_index: u32, 1595 total_fields: u32, 1596 }; 1597 1598 fn unionLayout(self: *DeclGen, ty: Type, maybe_active_field: ?usize) UnionLayout { 1599 const mod = self.module; 1600 const ip = &mod.intern_pool; 1601 const layout = ty.unionGetLayout(self.module); 1602 const union_obj = mod.typeToUnion(ty).?; 1603 1604 const active_field = maybe_active_field orelse layout.most_aligned_field; 1605 const active_field_ty = union_obj.field_types.get(ip)[active_field].toType(); 1606 1607 var union_layout = UnionLayout{ 1608 .active_field = @intCast(active_field), 1609 .active_field_ty = active_field_ty, 1610 .payload_size = @intCast(layout.payload_size), 1611 .tag_size = @intCast(layout.tag_size), 1612 .tag_index = undefined, 1613 .active_field_size = undefined, 1614 .active_field_index = undefined, 1615 .payload_padding_size = undefined, 1616 .payload_padding_index = undefined, 1617 .padding_size = @intCast(layout.padding), 1618 .padding_index = undefined, 1619 .total_fields = undefined, 1620 }; 1621 1622 union_layout.active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod)) 1623 @intCast(active_field_ty.abiSize(mod)) 1624 else 1625 0; 1626 union_layout.payload_padding_size = @intCast(layout.payload_size - union_layout.active_field_size); 1627 1628 const tag_first = layout.tag_align.compare(.gte, layout.payload_align); 1629 var field_index: u32 = 0; 1630 1631 if (union_layout.tag_size != 0 and tag_first) { 1632 union_layout.tag_index = field_index; 1633 field_index += 1; 1634 } 1635 1636 if (union_layout.active_field_size != 0) { 1637 union_layout.active_field_index = field_index; 1638 field_index += 1; 1639 } 1640 1641 if (union_layout.payload_padding_size != 0) { 1642 union_layout.payload_padding_index = field_index; 1643 field_index += 1; 1644 } 1645 1646 if (union_layout.tag_size != 0 and !tag_first) { 1647 union_layout.tag_index = field_index; 1648 field_index += 1; 1649 } 1650 1651 if (union_layout.padding_size != 0) { 1652 union_layout.padding_index = field_index; 1653 field_index += 1; 1654 } 1655 1656 union_layout.total_fields = field_index; 1657 1658 return union_layout; 1659 } 1660 1661 /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure. 1662 /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry- 1663 /// points. The test executor will then be able to invoke these to run the tests. 1664 /// Note that tests are lowered according to std.builtin.TestFn, which is `fn () anyerror!void`. 1665 /// (anyerror!void has the same layout as anyerror). 1666 /// Each test declaration generates a function like. 1667 /// %anyerror = OpTypeInt 0 16 1668 /// %p_anyerror = OpTypePointer CrossWorkgroup %anyerror 1669 /// %K = OpTypeFunction %void %p_anyerror 1670 /// 1671 /// %test = OpFunction %void %K 1672 /// %p_err = OpFunctionParameter %p_anyerror 1673 /// %lbl = OpLabel 1674 /// %result = OpFunctionCall %anyerror %func 1675 /// OpStore %p_err %result 1676 /// OpFunctionEnd 1677 /// TODO is to also write out the error as a function call parameter, and to somehow fetch 1678 /// the name of an error in the text executor. 1679 fn generateTestEntryPoint(self: *DeclGen, name: []const u8, spv_test_decl_index: SpvModule.Decl.Index) !void { 1680 const anyerror_ty_ref = try self.resolveType(Type.anyerror, .direct); 1681 const ptr_anyerror_ty_ref = try self.spv.ptrType(anyerror_ty_ref, .CrossWorkgroup); 1682 const void_ty_ref = try self.resolveType(Type.void, .direct); 1683 1684 const kernel_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{ 1685 .return_type = void_ty_ref, 1686 .parameters = &.{ptr_anyerror_ty_ref}, 1687 } }); 1688 1689 const test_id = self.spv.declPtr(spv_test_decl_index).result_id; 1690 1691 const spv_decl_index = try self.spv.allocDecl(.func); 1692 const kernel_id = self.spv.declPtr(spv_decl_index).result_id; 1693 1694 const error_id = self.spv.allocId(); 1695 const p_error_id = self.spv.allocId(); 1696 1697 const section = &self.spv.sections.functions; 1698 try section.emit(self.spv.gpa, .OpFunction, .{ 1699 .id_result_type = self.typeId(void_ty_ref), 1700 .id_result = kernel_id, 1701 .function_control = .{}, 1702 .function_type = self.typeId(kernel_proto_ty_ref), 1703 }); 1704 try section.emit(self.spv.gpa, .OpFunctionParameter, .{ 1705 .id_result_type = self.typeId(ptr_anyerror_ty_ref), 1706 .id_result = p_error_id, 1707 }); 1708 try section.emit(self.spv.gpa, .OpLabel, .{ 1709 .id_result = self.spv.allocId(), 1710 }); 1711 try section.emit(self.spv.gpa, .OpFunctionCall, .{ 1712 .id_result_type = self.typeId(anyerror_ty_ref), 1713 .id_result = error_id, 1714 .function = test_id, 1715 }); 1716 try section.emit(self.spv.gpa, .OpStore, .{ 1717 .pointer = p_error_id, 1718 .object = error_id, 1719 }); 1720 try section.emit(self.spv.gpa, .OpReturn, {}); 1721 try section.emit(self.spv.gpa, .OpFunctionEnd, {}); 1722 1723 try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index}); 1724 1725 // Just generate a quick other name because the intel runtime crashes when the entry- 1726 // point name is the same as a different OpName. 1727 const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name}); 1728 defer self.gpa.free(test_name); 1729 try self.spv.declareEntryPoint(spv_decl_index, test_name); 1730 } 1731 1732 fn genDecl(self: *DeclGen) !void { 1733 const mod = self.module; 1734 const ip = &mod.intern_pool; 1735 const decl = mod.declPtr(self.decl_index); 1736 const spv_decl_index = try self.object.resolveDecl(mod, self.decl_index); 1737 1738 const decl_id = self.spv.declPtr(spv_decl_index).result_id; 1739 1740 try self.base_line_stack.append(self.gpa, decl.src_line); 1741 1742 if (decl.val.getFunction(mod)) |_| { 1743 assert(decl.ty.zigTypeTag(mod) == .Fn); 1744 const fn_info = mod.typeToFunc(decl.ty).?; 1745 const return_ty_ref = try self.resolveFnReturnType(fn_info.return_type.toType()); 1746 1747 const prototype_id = try self.resolveTypeId(decl.ty); 1748 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ 1749 .id_result_type = self.typeId(return_ty_ref), 1750 .id_result = decl_id, 1751 .function_control = .{}, // TODO: We can set inline here if the type requires it. 1752 .function_type = prototype_id, 1753 }); 1754 1755 try self.args.ensureUnusedCapacity(self.gpa, fn_info.param_types.len); 1756 for (fn_info.param_types.get(ip)) |param_ty_index| { 1757 const param_ty = param_ty_index.toType(); 1758 if (!param_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; 1759 1760 const param_type_id = try self.resolveTypeId(param_ty); 1761 const arg_result_id = self.spv.allocId(); 1762 try self.func.prologue.emit(self.spv.gpa, .OpFunctionParameter, .{ 1763 .id_result_type = param_type_id, 1764 .id_result = arg_result_id, 1765 }); 1766 self.args.appendAssumeCapacity(arg_result_id); 1767 } 1768 1769 // TODO: This could probably be done in a better way... 1770 const root_block_id = self.spv.allocId(); 1771 1772 // The root block of a function declaration should appear before OpVariable instructions, 1773 // so it is generated into the function's prologue. 1774 try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{ 1775 .id_result = root_block_id, 1776 }); 1777 self.current_block_label_id = root_block_id; 1778 1779 const main_body = self.air.getMainBody(); 1780 try self.genBody(main_body); 1781 1782 // Append the actual code into the functions section. 1783 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); 1784 try self.spv.addFunction(spv_decl_index, self.func); 1785 1786 const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); 1787 try self.spv.debugName(decl_id, fqn); 1788 1789 // Temporarily generate a test kernel declaration if this is a test function. 1790 if (self.module.test_functions.contains(self.decl_index)) { 1791 try self.generateTestEntryPoint(fqn, spv_decl_index); 1792 } 1793 } else { 1794 const init_val = if (decl.val.getVariable(mod)) |payload| 1795 payload.init.toValue() 1796 else 1797 decl.val; 1798 1799 if (init_val.ip_index == .unreachable_value) { 1800 return self.todo("importing extern variables", .{}); 1801 } 1802 1803 // Currently, initializers for CrossWorkgroup variables is not implemented 1804 // in Mesa. Therefore we generate an initialization kernel instead. 1805 1806 const void_ty_ref = try self.resolveType(Type.void, .direct); 1807 1808 const initializer_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{ 1809 .return_type = void_ty_ref, 1810 .parameters = &.{}, 1811 } }); 1812 1813 // Generate the actual variable for the global... 1814 const final_storage_class = spvStorageClass(decl.@"addrspace"); 1815 const actual_storage_class = switch (final_storage_class) { 1816 .Generic => .CrossWorkgroup, 1817 else => final_storage_class, 1818 }; 1819 1820 const ty_ref = try self.resolveType(decl.ty, .indirect); 1821 const ptr_ty_ref = try self.spv.ptrType(ty_ref, actual_storage_class); 1822 1823 const begin = self.spv.beginGlobal(); 1824 try self.spv.globals.section.emit(self.spv.gpa, .OpVariable, .{ 1825 .id_result_type = self.typeId(ptr_ty_ref), 1826 .id_result = decl_id, 1827 .storage_class = actual_storage_class, 1828 }); 1829 1830 // Now emit the instructions that initialize the variable. 1831 const initializer_id = self.spv.allocId(); 1832 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ 1833 .id_result_type = self.typeId(void_ty_ref), 1834 .id_result = initializer_id, 1835 .function_control = .{}, 1836 .function_type = self.typeId(initializer_proto_ty_ref), 1837 }); 1838 const root_block_id = self.spv.allocId(); 1839 try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{ 1840 .id_result = root_block_id, 1841 }); 1842 self.current_block_label_id = root_block_id; 1843 1844 const val_id = try self.constant(decl.ty, init_val, .indirect); 1845 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 1846 .pointer = decl_id, 1847 .object = val_id, 1848 }); 1849 1850 // TODO: We should be able to get rid of this by now... 1851 self.spv.endGlobal(spv_decl_index, begin, decl_id, initializer_id); 1852 1853 try self.func.body.emit(self.spv.gpa, .OpReturn, {}); 1854 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); 1855 try self.spv.addFunction(spv_decl_index, self.func); 1856 1857 const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); 1858 try self.spv.debugName(decl_id, fqn); 1859 try self.spv.debugNameFmt(initializer_id, "initializer of {s}", .{fqn}); 1860 } 1861 } 1862 1863 fn intFromBool(self: *DeclGen, result_ty_ref: CacheRef, condition_id: IdRef) !IdRef { 1864 const zero_id = try self.constInt(result_ty_ref, 0); 1865 const one_id = try self.constInt(result_ty_ref, 1); 1866 const result_id = self.spv.allocId(); 1867 try self.func.body.emit(self.spv.gpa, .OpSelect, .{ 1868 .id_result_type = self.typeId(result_ty_ref), 1869 .id_result = result_id, 1870 .condition = condition_id, 1871 .object_1 = one_id, 1872 .object_2 = zero_id, 1873 }); 1874 return result_id; 1875 } 1876 1877 /// Convert representation from indirect (in memory) to direct (in 'register') 1878 /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct). 1879 fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { 1880 const mod = self.module; 1881 return switch (ty.zigTypeTag(mod)) { 1882 .Bool => blk: { 1883 const direct_bool_ty_ref = try self.resolveType(ty, .direct); 1884 const indirect_bool_ty_ref = try self.resolveType(ty, .indirect); 1885 const zero_id = try self.constInt(indirect_bool_ty_ref, 0); 1886 const result_id = self.spv.allocId(); 1887 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ 1888 .id_result_type = self.typeId(direct_bool_ty_ref), 1889 .id_result = result_id, 1890 .operand_1 = operand_id, 1891 .operand_2 = zero_id, 1892 }); 1893 break :blk result_id; 1894 }, 1895 else => operand_id, 1896 }; 1897 } 1898 1899 /// Convert representation from direct (in 'register) to direct (in memory) 1900 /// This converts the argument type from resolveType(ty, .direct) to resolveType(ty, .indirect). 1901 fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { 1902 const mod = self.module; 1903 return switch (ty.zigTypeTag(mod)) { 1904 .Bool => blk: { 1905 const indirect_bool_ty_ref = try self.resolveType(ty, .indirect); 1906 break :blk self.intFromBool(indirect_bool_ty_ref, operand_id); 1907 }, 1908 else => operand_id, 1909 }; 1910 } 1911 1912 fn extractField(self: *DeclGen, result_ty: Type, object: IdRef, field: u32) !IdRef { 1913 const result_ty_ref = try self.resolveType(result_ty, .indirect); 1914 const result_id = self.spv.allocId(); 1915 const indexes = [_]u32{field}; 1916 try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ 1917 .id_result_type = self.typeId(result_ty_ref), 1918 .id_result = result_id, 1919 .composite = object, 1920 .indexes = &indexes, 1921 }); 1922 // Convert bools; direct structs have their field types as indirect values. 1923 return try self.convertToDirect(result_ty, result_id); 1924 } 1925 1926 fn load(self: *DeclGen, value_ty: Type, ptr_id: IdRef, is_volatile: bool) !IdRef { 1927 const indirect_value_ty_ref = try self.resolveType(value_ty, .indirect); 1928 const result_id = self.spv.allocId(); 1929 const access = spec.MemoryAccess.Extended{ 1930 .Volatile = is_volatile, 1931 }; 1932 try self.func.body.emit(self.spv.gpa, .OpLoad, .{ 1933 .id_result_type = self.typeId(indirect_value_ty_ref), 1934 .id_result = result_id, 1935 .pointer = ptr_id, 1936 .memory_access = access, 1937 }); 1938 return try self.convertToDirect(value_ty, result_id); 1939 } 1940 1941 fn store(self: *DeclGen, value_ty: Type, ptr_id: IdRef, value_id: IdRef, is_volatile: bool) !void { 1942 const indirect_value_id = try self.convertToIndirect(value_ty, value_id); 1943 const access = spec.MemoryAccess.Extended{ 1944 .Volatile = is_volatile, 1945 }; 1946 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 1947 .pointer = ptr_id, 1948 .object = indirect_value_id, 1949 .memory_access = access, 1950 }); 1951 } 1952 1953 fn genBody(self: *DeclGen, body: []const Air.Inst.Index) Error!void { 1954 for (body) |inst| { 1955 try self.genInst(inst); 1956 } 1957 } 1958 1959 fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void { 1960 const mod = self.module; 1961 const ip = &mod.intern_pool; 1962 // TODO: remove now-redundant isUnused calls from AIR handler functions 1963 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) 1964 return; 1965 1966 const air_tags = self.air.instructions.items(.tag); 1967 const maybe_result_id: ?IdRef = switch (air_tags[inst]) { 1968 // zig fmt: off 1969 .add, .add_wrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd, true), 1970 .sub, .sub_wrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub, true), 1971 .mul, .mul_wrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul, true), 1972 1973 .div_float, 1974 .div_float_optimized, 1975 // TODO: Check that this is the right operation. 1976 .div_trunc, 1977 .div_trunc_optimized, 1978 => try self.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv, false), 1979 // TODO: Check if this is the right operation 1980 // TODO: Make airArithOp for rem not emit a mask for the LHS. 1981 .rem, 1982 .rem_optimized, 1983 => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem, false), 1984 1985 .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan), 1986 .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan), 1987 1988 .shuffle => try self.airShuffle(inst), 1989 1990 .ptr_add => try self.airPtrAdd(inst), 1991 .ptr_sub => try self.airPtrSub(inst), 1992 1993 .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), 1994 .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr), 1995 .xor => try self.airBinOpSimple(inst, .OpBitwiseXor), 1996 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd), 1997 .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr), 1998 1999 .shl => try self.airShift(inst, .OpShiftLeftLogical), 2000 2001 .min => try self.airMinMax(inst, .lt), 2002 .max => try self.airMinMax(inst, .gt), 2003 2004 .bitcast => try self.airBitCast(inst), 2005 .intcast, .trunc => try self.airIntCast(inst), 2006 .int_from_ptr => try self.airIntFromPtr(inst), 2007 .float_from_int => try self.airFloatFromInt(inst), 2008 .int_from_float => try self.airIntFromFloat(inst), 2009 .fpext, .fptrunc => try self.airFloatCast(inst), 2010 .not => try self.airNot(inst), 2011 2012 .array_to_slice => try self.airArrayToSlice(inst), 2013 .slice => try self.airSlice(inst), 2014 .aggregate_init => try self.airAggregateInit(inst), 2015 .memcpy => return self.airMemcpy(inst), 2016 2017 .slice_ptr => try self.airSliceField(inst, 0), 2018 .slice_len => try self.airSliceField(inst, 1), 2019 .slice_elem_ptr => try self.airSliceElemPtr(inst), 2020 .slice_elem_val => try self.airSliceElemVal(inst), 2021 .ptr_elem_ptr => try self.airPtrElemPtr(inst), 2022 .ptr_elem_val => try self.airPtrElemVal(inst), 2023 .array_elem_val => try self.airArrayElemVal(inst), 2024 2025 .set_union_tag => return self.airSetUnionTag(inst), 2026 .get_union_tag => try self.airGetUnionTag(inst), 2027 .union_init => try self.airUnionInit(inst), 2028 2029 .struct_field_val => try self.airStructFieldVal(inst), 2030 .field_parent_ptr => try self.airFieldParentPtr(inst), 2031 2032 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), 2033 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), 2034 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), 2035 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), 2036 2037 .cmp_eq => try self.airCmp(inst, .eq), 2038 .cmp_neq => try self.airCmp(inst, .neq), 2039 .cmp_gt => try self.airCmp(inst, .gt), 2040 .cmp_gte => try self.airCmp(inst, .gte), 2041 .cmp_lt => try self.airCmp(inst, .lt), 2042 .cmp_lte => try self.airCmp(inst, .lte), 2043 .cmp_vector => try self.airVectorCmp(inst), 2044 2045 .arg => self.airArg(), 2046 .alloc => try self.airAlloc(inst), 2047 // TODO: We probably need to have a special implementation of this for the C abi. 2048 .ret_ptr => try self.airAlloc(inst), 2049 .block => try self.airBlock(inst), 2050 2051 .load => try self.airLoad(inst), 2052 .store, .store_safe => return self.airStore(inst), 2053 2054 .br => return self.airBr(inst), 2055 .breakpoint => return, 2056 .cond_br => return self.airCondBr(inst), 2057 .loop => return self.airLoop(inst), 2058 .ret => return self.airRet(inst), 2059 .ret_load => return self.airRetLoad(inst), 2060 .@"try" => try self.airTry(inst), 2061 .switch_br => return self.airSwitchBr(inst), 2062 .unreach, .trap => return self.airUnreach(), 2063 2064 .dbg_stmt => return self.airDbgStmt(inst), 2065 .dbg_inline_begin => return self.airDbgInlineBegin(inst), 2066 .dbg_inline_end => return self.airDbgInlineEnd(inst), 2067 .dbg_var_ptr, .dbg_var_val => return self.airDbgVar(inst), 2068 .dbg_block_begin => return, 2069 .dbg_block_end => return, 2070 2071 .unwrap_errunion_err => try self.airErrUnionErr(inst), 2072 .unwrap_errunion_payload => try self.airErrUnionPayload(inst), 2073 .wrap_errunion_err => try self.airWrapErrUnionErr(inst), 2074 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), 2075 2076 .is_null => try self.airIsNull(inst, .is_null), 2077 .is_non_null => try self.airIsNull(inst, .is_non_null), 2078 .is_err => try self.airIsErr(inst, .is_err), 2079 .is_non_err => try self.airIsErr(inst, .is_non_err), 2080 2081 .optional_payload => try self.airUnwrapOptional(inst), 2082 .wrap_optional => try self.airWrapOptional(inst), 2083 2084 .assembly => try self.airAssembly(inst), 2085 2086 .call => try self.airCall(inst, .auto), 2087 .call_always_tail => try self.airCall(inst, .always_tail), 2088 .call_never_tail => try self.airCall(inst, .never_tail), 2089 .call_never_inline => try self.airCall(inst, .never_inline), 2090 // zig fmt: on 2091 2092 else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}), 2093 }; 2094 2095 const result_id = maybe_result_id orelse return; 2096 try self.inst_results.putNoClobber(self.gpa, inst, result_id); 2097 } 2098 2099 fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef { 2100 const mod = self.module; 2101 2102 if (ty.isVector(mod)) { 2103 const child_ty = ty.childType(mod); 2104 const vector_len = ty.vectorLen(mod); 2105 2106 var constituents = try self.gpa.alloc(IdRef, vector_len); 2107 defer self.gpa.free(constituents); 2108 2109 for (constituents, 0..) |*constituent, i| { 2110 const lhs_index_id = try self.extractField(child_ty, lhs_id, @intCast(i)); 2111 const rhs_index_id = try self.extractField(child_ty, rhs_id, @intCast(i)); 2112 const result_id = try self.binOpSimple(child_ty, lhs_index_id, rhs_index_id, opcode); 2113 constituent.* = try self.convertToIndirect(child_ty, result_id); 2114 } 2115 2116 const result_ty = try self.resolveType(child_ty, .indirect); 2117 const result_ty_ref = try self.spv.arrayType(vector_len, result_ty); 2118 return try self.constructArray(result_ty_ref, constituents); 2119 } 2120 2121 const result_id = self.spv.allocId(); 2122 const result_type_id = try self.resolveTypeId(ty); 2123 try self.func.body.emit(self.spv.gpa, opcode, .{ 2124 .id_result_type = result_type_id, 2125 .id_result = result_id, 2126 .operand_1 = lhs_id, 2127 .operand_2 = rhs_id, 2128 }); 2129 return result_id; 2130 } 2131 2132 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef { 2133 if (self.liveness.isUnused(inst)) return null; 2134 2135 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2136 const lhs_id = try self.resolve(bin_op.lhs); 2137 const rhs_id = try self.resolve(bin_op.rhs); 2138 const ty = self.typeOf(bin_op.lhs); 2139 2140 return try self.binOpSimple(ty, lhs_id, rhs_id, opcode); 2141 } 2142 2143 fn airShift(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef { 2144 if (self.liveness.isUnused(inst)) return null; 2145 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2146 const lhs_id = try self.resolve(bin_op.lhs); 2147 const rhs_id = try self.resolve(bin_op.rhs); 2148 const result_type_id = try self.resolveTypeId(self.typeOfIndex(inst)); 2149 2150 // the shift and the base must be the same type in SPIR-V, but in Zig the shift is a smaller int. 2151 const shift_id = self.spv.allocId(); 2152 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ 2153 .id_result_type = result_type_id, 2154 .id_result = shift_id, 2155 .unsigned_value = rhs_id, 2156 }); 2157 2158 const result_id = self.spv.allocId(); 2159 try self.func.body.emit(self.spv.gpa, opcode, .{ 2160 .id_result_type = result_type_id, 2161 .id_result = result_id, 2162 .base = lhs_id, 2163 .shift = shift_id, 2164 }); 2165 return result_id; 2166 } 2167 2168 fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef { 2169 if (self.liveness.isUnused(inst)) return null; 2170 2171 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2172 const lhs_id = try self.resolve(bin_op.lhs); 2173 const rhs_id = try self.resolve(bin_op.rhs); 2174 const result_ty = self.typeOfIndex(inst); 2175 const result_ty_ref = try self.resolveType(result_ty, .direct); 2176 2177 const info = try self.arithmeticTypeInfo(result_ty); 2178 // TODO: Use fmin for OpenCL 2179 const cmp_id = try self.cmp(op, result_ty, lhs_id, rhs_id); 2180 const selection_id = switch (info.class) { 2181 .float => blk: { 2182 // cmp uses OpFOrd. When we have 0 [<>] nan this returns false, 2183 // but we want it to pick lhs. Therefore we also have to check if 2184 // rhs is nan. We don't need to care about the result when both 2185 // are nan. 2186 const rhs_is_nan_id = self.spv.allocId(); 2187 const bool_ty_ref = try self.resolveType(Type.bool, .direct); 2188 try self.func.body.emit(self.spv.gpa, .OpIsNan, .{ 2189 .id_result_type = self.typeId(bool_ty_ref), 2190 .id_result = rhs_is_nan_id, 2191 .x = rhs_id, 2192 }); 2193 const float_cmp_id = self.spv.allocId(); 2194 try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{ 2195 .id_result_type = self.typeId(bool_ty_ref), 2196 .id_result = float_cmp_id, 2197 .operand_1 = cmp_id, 2198 .operand_2 = rhs_is_nan_id, 2199 }); 2200 break :blk float_cmp_id; 2201 }, 2202 else => cmp_id, 2203 }; 2204 2205 const result_id = self.spv.allocId(); 2206 try self.func.body.emit(self.spv.gpa, .OpSelect, .{ 2207 .id_result_type = self.typeId(result_ty_ref), 2208 .id_result = result_id, 2209 .condition = selection_id, 2210 .object_1 = lhs_id, 2211 .object_2 = rhs_id, 2212 }); 2213 return result_id; 2214 } 2215 2216 /// This function canonicalizes a "strange" integer value: 2217 /// For unsigned integers, the value is masked so that only the relevant bits can contain 2218 /// non-zeros. 2219 /// For signed integers, the value is also sign extended. 2220 fn normalizeInt(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef { 2221 assert(info.class != .composite_integer); // TODO 2222 if (info.bits == info.backing_bits) { 2223 return value_id; 2224 } 2225 2226 switch (info.signedness) { 2227 .unsigned => { 2228 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; 2229 const result_id = self.spv.allocId(); 2230 const mask_id = try self.constInt(ty_ref, mask_value); 2231 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{ 2232 .id_result_type = self.typeId(ty_ref), 2233 .id_result = result_id, 2234 .operand_1 = value_id, 2235 .operand_2 = mask_id, 2236 }); 2237 return result_id; 2238 }, 2239 .signed => { 2240 // Shift left and right so that we can copy the sight bit that way. 2241 const shift_amt_id = try self.constInt(ty_ref, info.backing_bits - info.bits); 2242 const left_id = self.spv.allocId(); 2243 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ 2244 .id_result_type = self.typeId(ty_ref), 2245 .id_result = left_id, 2246 .base = value_id, 2247 .shift = shift_amt_id, 2248 }); 2249 const right_id = self.spv.allocId(); 2250 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ 2251 .id_result_type = self.typeId(ty_ref), 2252 .id_result = right_id, 2253 .base = left_id, 2254 .shift = shift_amt_id, 2255 }); 2256 return right_id; 2257 }, 2258 } 2259 } 2260 2261 fn airArithOp( 2262 self: *DeclGen, 2263 inst: Air.Inst.Index, 2264 comptime fop: Opcode, 2265 comptime sop: Opcode, 2266 comptime uop: Opcode, 2267 /// true if this operation holds under modular arithmetic. 2268 comptime modular: bool, 2269 ) !?IdRef { 2270 if (self.liveness.isUnused(inst)) return null; 2271 2272 // LHS and RHS are guaranteed to have the same type, and AIR guarantees 2273 // the result to be the same as the LHS and RHS, which matches SPIR-V. 2274 const ty = self.typeOfIndex(inst); 2275 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2276 const lhs_id = try self.resolve(bin_op.lhs); 2277 const rhs_id = try self.resolve(bin_op.rhs); 2278 2279 assert(self.typeOf(bin_op.lhs).eql(ty, self.module)); 2280 assert(self.typeOf(bin_op.rhs).eql(ty, self.module)); 2281 2282 return try self.arithOp(ty, lhs_id, rhs_id, fop, sop, uop, modular); 2283 } 2284 2285 fn arithOp( 2286 self: *DeclGen, 2287 ty: Type, 2288 lhs_id_: IdRef, 2289 rhs_id_: IdRef, 2290 comptime fop: Opcode, 2291 comptime sop: Opcode, 2292 comptime uop: Opcode, 2293 /// true if this operation holds under modular arithmetic. 2294 comptime modular: bool, 2295 ) !IdRef { 2296 var rhs_id = rhs_id_; 2297 var lhs_id = lhs_id_; 2298 2299 const mod = self.module; 2300 const result_ty_ref = try self.resolveType(ty, .direct); 2301 2302 if (ty.isVector(mod)) { 2303 const child_ty = ty.childType(mod); 2304 const vector_len = ty.vectorLen(mod); 2305 var constituents = try self.gpa.alloc(IdRef, vector_len); 2306 defer self.gpa.free(constituents); 2307 2308 for (constituents, 0..) |*constituent, i| { 2309 const lhs_index_id = try self.extractField(child_ty, lhs_id, @intCast(i)); 2310 const rhs_index_id = try self.extractField(child_ty, rhs_id, @intCast(i)); 2311 constituent.* = try self.arithOp(child_ty, lhs_index_id, rhs_index_id, fop, sop, uop, modular); 2312 } 2313 2314 return self.constructArray(result_ty_ref, constituents); 2315 } 2316 2317 // Binary operations are generally applicable to both scalar and vector operations 2318 // in SPIR-V, but int and float versions of operations require different opcodes. 2319 const info = try self.arithmeticTypeInfo(ty); 2320 2321 const opcode_index: usize = switch (info.class) { 2322 .composite_integer => { 2323 return self.todo("binary operations for composite integers", .{}); 2324 }, 2325 .strange_integer => blk: { 2326 if (!modular) { 2327 lhs_id = try self.normalizeInt(result_ty_ref, lhs_id, info); 2328 rhs_id = try self.normalizeInt(result_ty_ref, rhs_id, info); 2329 } 2330 break :blk switch (info.signedness) { 2331 .signed => @as(usize, 1), 2332 .unsigned => @as(usize, 2), 2333 }; 2334 }, 2335 .integer => switch (info.signedness) { 2336 .signed => @as(usize, 1), 2337 .unsigned => @as(usize, 2), 2338 }, 2339 .float => 0, 2340 .bool => unreachable, 2341 }; 2342 2343 const result_id = self.spv.allocId(); 2344 const operands = .{ 2345 .id_result_type = self.typeId(result_ty_ref), 2346 .id_result = result_id, 2347 .operand_1 = lhs_id, 2348 .operand_2 = rhs_id, 2349 }; 2350 2351 switch (opcode_index) { 2352 0 => try self.func.body.emit(self.spv.gpa, fop, operands), 2353 1 => try self.func.body.emit(self.spv.gpa, sop, operands), 2354 2 => try self.func.body.emit(self.spv.gpa, uop, operands), 2355 else => unreachable, 2356 } 2357 // TODO: Trap on overflow? Probably going to be annoying. 2358 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. 2359 2360 return result_id; 2361 } 2362 2363 fn airAddSubOverflow( 2364 self: *DeclGen, 2365 inst: Air.Inst.Index, 2366 comptime add: Opcode, 2367 comptime ucmp: Opcode, 2368 comptime scmp: Opcode, 2369 ) !?IdRef { 2370 if (self.liveness.isUnused(inst)) return null; 2371 2372 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2373 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 2374 const lhs = try self.resolve(extra.lhs); 2375 const rhs = try self.resolve(extra.rhs); 2376 2377 const operand_ty = self.typeOf(extra.lhs); 2378 const result_ty = self.typeOfIndex(inst); 2379 2380 const info = try self.arithmeticTypeInfo(operand_ty); 2381 switch (info.class) { 2382 .composite_integer => return self.todo("overflow ops for composite integers", .{}), 2383 .strange_integer => return self.todo("overflow ops for strange integers", .{}), 2384 .integer => {}, 2385 .float, .bool => unreachable, 2386 } 2387 2388 // The operand type must be the same as the result type in SPIR-V, which 2389 // is the same as in Zig. 2390 const operand_ty_ref = try self.resolveType(operand_ty, .direct); 2391 const operand_ty_id = self.typeId(operand_ty_ref); 2392 2393 const bool_ty_ref = try self.resolveType(Type.bool, .direct); 2394 2395 const ov_ty = result_ty.structFieldType(1, self.module); 2396 // Note: result is stored in a struct, so indirect representation. 2397 const ov_ty_ref = try self.resolveType(ov_ty, .indirect); 2398 2399 // TODO: Operations other than addition. 2400 const value_id = self.spv.allocId(); 2401 try self.func.body.emit(self.spv.gpa, add, .{ 2402 .id_result_type = operand_ty_id, 2403 .id_result = value_id, 2404 .operand_1 = lhs, 2405 .operand_2 = rhs, 2406 }); 2407 2408 const overflowed_id = switch (info.signedness) { 2409 .unsigned => blk: { 2410 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which. 2411 // For subtraction the conditions need to be swapped. 2412 const overflowed_id = self.spv.allocId(); 2413 try self.func.body.emit(self.spv.gpa, ucmp, .{ 2414 .id_result_type = self.typeId(bool_ty_ref), 2415 .id_result = overflowed_id, 2416 .operand_1 = value_id, 2417 .operand_2 = lhs, 2418 }); 2419 break :blk overflowed_id; 2420 }, 2421 .signed => blk: { 2422 // lhs - rhs 2423 // For addition, overflow happened if: 2424 // - rhs is negative and value > lhs 2425 // - rhs is positive and value < lhs 2426 // This can be shortened to: 2427 // (rhs < 0 and value > lhs) or (rhs >= 0 and value <= lhs) 2428 // = (rhs < 0) == (value > lhs) 2429 // = (rhs < 0) == (lhs < value) 2430 // Note that signed overflow is also wrapping in spir-v. 2431 // For subtraction, overflow happened if: 2432 // - rhs is negative and value < lhs 2433 // - rhs is positive and value > lhs 2434 // This can be shortened to: 2435 // (rhs < 0 and value < lhs) or (rhs >= 0 and value >= lhs) 2436 // = (rhs < 0) == (value < lhs) 2437 // = (rhs < 0) == (lhs > value) 2438 2439 const rhs_lt_zero_id = self.spv.allocId(); 2440 const zero_id = try self.constInt(operand_ty_ref, 0); 2441 try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ 2442 .id_result_type = self.typeId(bool_ty_ref), 2443 .id_result = rhs_lt_zero_id, 2444 .operand_1 = rhs, 2445 .operand_2 = zero_id, 2446 }); 2447 2448 const value_gt_lhs_id = self.spv.allocId(); 2449 try self.func.body.emit(self.spv.gpa, scmp, .{ 2450 .id_result_type = self.typeId(bool_ty_ref), 2451 .id_result = value_gt_lhs_id, 2452 .operand_1 = lhs, 2453 .operand_2 = value_id, 2454 }); 2455 2456 const overflowed_id = self.spv.allocId(); 2457 try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{ 2458 .id_result_type = self.typeId(bool_ty_ref), 2459 .id_result = overflowed_id, 2460 .operand_1 = rhs_lt_zero_id, 2461 .operand_2 = value_gt_lhs_id, 2462 }); 2463 break :blk overflowed_id; 2464 }, 2465 }; 2466 2467 // Construct the struct that Zig wants as result. 2468 // The value should already be the correct type. 2469 const ov_id = try self.intFromBool(ov_ty_ref, overflowed_id); 2470 const result_ty_ref = try self.resolveType(result_ty, .direct); 2471 return try self.constructStruct(result_ty_ref, &.{ 2472 value_id, 2473 ov_id, 2474 }); 2475 } 2476 2477 fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2478 const mod = self.module; 2479 if (self.liveness.isUnused(inst)) return null; 2480 const ty = self.typeOfIndex(inst); 2481 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2482 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; 2483 const a = try self.resolve(extra.a); 2484 const b = try self.resolve(extra.b); 2485 const mask = extra.mask.toValue(); 2486 const mask_len = extra.mask_len; 2487 const a_len = self.typeOf(extra.a).vectorLen(mod); 2488 2489 const result_id = self.spv.allocId(); 2490 const result_type_id = try self.resolveTypeId(ty); 2491 // Similar to LLVM, SPIR-V uses indices larger than the length of the first vector 2492 // to index into the second vector. 2493 try self.func.body.emitRaw(self.spv.gpa, .OpVectorShuffle, 4 + mask_len); 2494 self.func.body.writeOperand(spec.IdResultType, result_type_id); 2495 self.func.body.writeOperand(spec.IdResult, result_id); 2496 self.func.body.writeOperand(spec.IdRef, a); 2497 self.func.body.writeOperand(spec.IdRef, b); 2498 2499 var i: usize = 0; 2500 while (i < mask_len) : (i += 1) { 2501 const elem = try mask.elemValue(mod, i); 2502 if (elem.isUndef(mod)) { 2503 self.func.body.writeOperand(spec.LiteralInteger, 0xFFFF_FFFF); 2504 } else { 2505 const int = elem.toSignedInt(mod); 2506 const unsigned = if (int >= 0) @as(u32, @intCast(int)) else @as(u32, @intCast(~int + a_len)); 2507 self.func.body.writeOperand(spec.LiteralInteger, unsigned); 2508 } 2509 } 2510 return result_id; 2511 } 2512 2513 fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef { 2514 const index_ty_ref = try self.intType(.unsigned, 32); 2515 const ids = try self.gpa.alloc(IdRef, indices.len); 2516 errdefer self.gpa.free(ids); 2517 for (indices, ids) |index, *id| { 2518 id.* = try self.constInt(index_ty_ref, index); 2519 } 2520 2521 return ids; 2522 } 2523 2524 fn accessChainId( 2525 self: *DeclGen, 2526 result_ty_ref: CacheRef, 2527 base: IdRef, 2528 indices: []const IdRef, 2529 ) !IdRef { 2530 const result_id = self.spv.allocId(); 2531 try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ 2532 .id_result_type = self.typeId(result_ty_ref), 2533 .id_result = result_id, 2534 .base = base, 2535 .indexes = indices, 2536 }); 2537 return result_id; 2538 } 2539 2540 /// AccessChain is essentially PtrAccessChain with 0 as initial argument. The effective 2541 /// difference lies in whether the resulting type of the first dereference will be the 2542 /// same as that of the base pointer, or that of a dereferenced base pointer. AccessChain 2543 /// is the latter and PtrAccessChain is the former. 2544 fn accessChain( 2545 self: *DeclGen, 2546 result_ty_ref: CacheRef, 2547 base: IdRef, 2548 indices: []const u32, 2549 ) !IdRef { 2550 const ids = try self.indicesToIds(indices); 2551 defer self.gpa.free(ids); 2552 return try self.accessChainId(result_ty_ref, base, ids); 2553 } 2554 2555 fn ptrAccessChain( 2556 self: *DeclGen, 2557 result_ty_ref: CacheRef, 2558 base: IdRef, 2559 element: IdRef, 2560 indices: []const u32, 2561 ) !IdRef { 2562 const ids = try self.indicesToIds(indices); 2563 defer self.gpa.free(ids); 2564 2565 const result_id = self.spv.allocId(); 2566 try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{ 2567 .id_result_type = self.typeId(result_ty_ref), 2568 .id_result = result_id, 2569 .base = base, 2570 .element = element, 2571 .indexes = ids, 2572 }); 2573 return result_id; 2574 } 2575 2576 fn ptrAdd(self: *DeclGen, result_ty: Type, ptr_ty: Type, ptr_id: IdRef, offset_id: IdRef) !IdRef { 2577 const mod = self.module; 2578 const result_ty_ref = try self.resolveType(result_ty, .direct); 2579 2580 switch (ptr_ty.ptrSize(mod)) { 2581 .One => { 2582 // Pointer to array 2583 // TODO: Is this correct? 2584 return try self.accessChainId(result_ty_ref, ptr_id, &.{offset_id}); 2585 }, 2586 .C, .Many => { 2587 return try self.ptrAccessChain(result_ty_ref, ptr_id, offset_id, &.{}); 2588 }, 2589 .Slice => { 2590 // TODO: This is probably incorrect. A slice should be returned here, though this is what llvm does. 2591 const slice_ptr_id = try self.extractField(result_ty, ptr_id, 0); 2592 return try self.ptrAccessChain(result_ty_ref, slice_ptr_id, offset_id, &.{}); 2593 }, 2594 } 2595 } 2596 2597 fn airPtrAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2598 if (self.liveness.isUnused(inst)) return null; 2599 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2600 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 2601 const ptr_id = try self.resolve(bin_op.lhs); 2602 const offset_id = try self.resolve(bin_op.rhs); 2603 const ptr_ty = self.typeOf(bin_op.lhs); 2604 const result_ty = self.typeOfIndex(inst); 2605 2606 return try self.ptrAdd(result_ty, ptr_ty, ptr_id, offset_id); 2607 } 2608 2609 fn airPtrSub(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2610 if (self.liveness.isUnused(inst)) return null; 2611 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2612 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 2613 const ptr_id = try self.resolve(bin_op.lhs); 2614 const ptr_ty = self.typeOf(bin_op.lhs); 2615 const offset_id = try self.resolve(bin_op.rhs); 2616 const offset_ty = self.typeOf(bin_op.rhs); 2617 const offset_ty_ref = try self.resolveType(offset_ty, .direct); 2618 const result_ty = self.typeOfIndex(inst); 2619 2620 const negative_offset_id = self.spv.allocId(); 2621 try self.func.body.emit(self.spv.gpa, .OpSNegate, .{ 2622 .id_result_type = self.typeId(offset_ty_ref), 2623 .id_result = negative_offset_id, 2624 .operand = offset_id, 2625 }); 2626 return try self.ptrAdd(result_ty, ptr_ty, ptr_id, negative_offset_id); 2627 } 2628 2629 fn cmp( 2630 self: *DeclGen, 2631 op: std.math.CompareOperator, 2632 ty: Type, 2633 lhs_id: IdRef, 2634 rhs_id: IdRef, 2635 ) !IdRef { 2636 const mod = self.module; 2637 var cmp_lhs_id = lhs_id; 2638 var cmp_rhs_id = rhs_id; 2639 const bool_ty_ref = try self.resolveType(Type.bool, .direct); 2640 const op_ty = switch (ty.zigTypeTag(mod)) { 2641 .Int, .Bool, .Float => ty, 2642 .Enum => ty.intTagType(mod), 2643 .ErrorSet => Type.u16, 2644 .Pointer => blk: { 2645 // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are 2646 // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using 2647 // OpConvertPtrToU... 2648 cmp_lhs_id = self.spv.allocId(); 2649 cmp_rhs_id = self.spv.allocId(); 2650 2651 const usize_ty_id = self.typeId(try self.sizeType()); 2652 2653 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ 2654 .id_result_type = usize_ty_id, 2655 .id_result = cmp_lhs_id, 2656 .pointer = lhs_id, 2657 }); 2658 2659 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ 2660 .id_result_type = usize_ty_id, 2661 .id_result = cmp_rhs_id, 2662 .pointer = rhs_id, 2663 }); 2664 2665 break :blk Type.usize; 2666 }, 2667 .Optional => { 2668 const payload_ty = ty.optionalChild(mod); 2669 if (ty.optionalReprIsPayload(mod)) { 2670 assert(payload_ty.hasRuntimeBitsIgnoreComptime(mod)); 2671 assert(!payload_ty.isSlice(mod)); 2672 return self.cmp(op, payload_ty, lhs_id, rhs_id); 2673 } 2674 2675 const lhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) 2676 try self.extractField(Type.bool, lhs_id, 1) 2677 else 2678 try self.convertToDirect(Type.bool, lhs_id); 2679 2680 const rhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) 2681 try self.extractField(Type.bool, rhs_id, 1) 2682 else 2683 try self.convertToDirect(Type.bool, rhs_id); 2684 2685 const valid_cmp_id = try self.cmp(op, Type.bool, lhs_valid_id, rhs_valid_id); 2686 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { 2687 return valid_cmp_id; 2688 } 2689 2690 // TODO: Should we short circuit here? It shouldn't affect correctness, but 2691 // perhaps it will generate more efficient code. 2692 2693 const lhs_pl_id = try self.extractField(payload_ty, lhs_id, 0); 2694 const rhs_pl_id = try self.extractField(payload_ty, rhs_id, 0); 2695 2696 const pl_cmp_id = try self.cmp(op, payload_ty, lhs_pl_id, rhs_pl_id); 2697 2698 // op == .eq => lhs_valid == rhs_valid && lhs_pl == rhs_pl 2699 // op == .neq => lhs_valid != rhs_valid || lhs_pl != rhs_pl 2700 2701 const result_id = self.spv.allocId(); 2702 const args = .{ 2703 .id_result_type = self.typeId(bool_ty_ref), 2704 .id_result = result_id, 2705 .operand_1 = valid_cmp_id, 2706 .operand_2 = pl_cmp_id, 2707 }; 2708 switch (op) { 2709 .eq => try self.func.body.emit(self.spv.gpa, .OpLogicalAnd, args), 2710 .neq => try self.func.body.emit(self.spv.gpa, .OpLogicalOr, args), 2711 else => unreachable, 2712 } 2713 return result_id; 2714 }, 2715 .Vector => { 2716 const child_ty = ty.childType(mod); 2717 const vector_len = ty.vectorLen(mod); 2718 const bool_ty_ref_indirect = try self.resolveType(Type.bool, .indirect); 2719 2720 var constituents = try self.gpa.alloc(IdRef, vector_len); 2721 defer self.gpa.free(constituents); 2722 2723 for (constituents, 0..) |*constituent, i| { 2724 const lhs_index_id = try self.extractField(child_ty, cmp_lhs_id, @intCast(i)); 2725 const rhs_index_id = try self.extractField(child_ty, cmp_rhs_id, @intCast(i)); 2726 const result_id = try self.cmp(op, child_ty, lhs_index_id, rhs_index_id); 2727 constituent.* = try self.convertToIndirect(Type.bool, result_id); 2728 } 2729 2730 const result_ty_ref = try self.spv.arrayType(vector_len, bool_ty_ref_indirect); 2731 return try self.constructArray(result_ty_ref, constituents); 2732 }, 2733 else => unreachable, 2734 }; 2735 2736 const opcode: Opcode = opcode: { 2737 const info = try self.arithmeticTypeInfo(op_ty); 2738 const signedness = switch (info.class) { 2739 .composite_integer => { 2740 return self.todo("binary operations for composite integers", .{}); 2741 }, 2742 .float => break :opcode switch (op) { 2743 .eq => .OpFOrdEqual, 2744 .neq => .OpFUnordNotEqual, 2745 .lt => .OpFOrdLessThan, 2746 .lte => .OpFOrdLessThanEqual, 2747 .gt => .OpFOrdGreaterThan, 2748 .gte => .OpFOrdGreaterThanEqual, 2749 }, 2750 .bool => break :opcode switch (op) { 2751 .eq => .OpLogicalEqual, 2752 .neq => .OpLogicalNotEqual, 2753 else => unreachable, 2754 }, 2755 .strange_integer => sign: { 2756 const op_ty_ref = try self.resolveType(op_ty, .direct); 2757 // Mask operands before performing comparison. 2758 cmp_lhs_id = try self.normalizeInt(op_ty_ref, cmp_lhs_id, info); 2759 cmp_rhs_id = try self.normalizeInt(op_ty_ref, cmp_rhs_id, info); 2760 break :sign info.signedness; 2761 }, 2762 .integer => info.signedness, 2763 }; 2764 2765 break :opcode switch (signedness) { 2766 .unsigned => switch (op) { 2767 .eq => .OpIEqual, 2768 .neq => .OpINotEqual, 2769 .lt => .OpULessThan, 2770 .lte => .OpULessThanEqual, 2771 .gt => .OpUGreaterThan, 2772 .gte => .OpUGreaterThanEqual, 2773 }, 2774 .signed => switch (op) { 2775 .eq => .OpIEqual, 2776 .neq => .OpINotEqual, 2777 .lt => .OpSLessThan, 2778 .lte => .OpSLessThanEqual, 2779 .gt => .OpSGreaterThan, 2780 .gte => .OpSGreaterThanEqual, 2781 }, 2782 }; 2783 }; 2784 2785 const result_id = self.spv.allocId(); 2786 try self.func.body.emitRaw(self.spv.gpa, opcode, 4); 2787 self.func.body.writeOperand(spec.IdResultType, self.typeId(bool_ty_ref)); 2788 self.func.body.writeOperand(spec.IdResult, result_id); 2789 self.func.body.writeOperand(spec.IdResultType, cmp_lhs_id); 2790 self.func.body.writeOperand(spec.IdResultType, cmp_rhs_id); 2791 return result_id; 2792 } 2793 2794 fn airCmp( 2795 self: *DeclGen, 2796 inst: Air.Inst.Index, 2797 comptime op: std.math.CompareOperator, 2798 ) !?IdRef { 2799 if (self.liveness.isUnused(inst)) return null; 2800 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2801 const lhs_id = try self.resolve(bin_op.lhs); 2802 const rhs_id = try self.resolve(bin_op.rhs); 2803 const ty = self.typeOf(bin_op.lhs); 2804 2805 return try self.cmp(op, ty, lhs_id, rhs_id); 2806 } 2807 2808 fn airVectorCmp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2809 if (self.liveness.isUnused(inst)) return null; 2810 2811 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2812 const vec_cmp = self.air.extraData(Air.VectorCmp, ty_pl.payload).data; 2813 const lhs_id = try self.resolve(vec_cmp.lhs); 2814 const rhs_id = try self.resolve(vec_cmp.rhs); 2815 const op = vec_cmp.compareOperator(); 2816 const ty = self.typeOf(vec_cmp.lhs); 2817 2818 return try self.cmp(op, ty, lhs_id, rhs_id); 2819 } 2820 2821 fn bitCast( 2822 self: *DeclGen, 2823 dst_ty: Type, 2824 src_ty: Type, 2825 src_id: IdRef, 2826 ) !IdRef { 2827 const mod = self.module; 2828 const src_ty_ref = try self.resolveType(src_ty, .direct); 2829 const dst_ty_ref = try self.resolveType(dst_ty, .direct); 2830 if (src_ty_ref == dst_ty_ref) { 2831 return src_id; 2832 } 2833 2834 // TODO: Some more cases are missing here 2835 // See fn bitCast in llvm.zig 2836 2837 if (src_ty.zigTypeTag(mod) == .Int and dst_ty.isPtrAtRuntime(mod)) { 2838 const result_id = self.spv.allocId(); 2839 try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{ 2840 .id_result_type = self.typeId(dst_ty_ref), 2841 .id_result = result_id, 2842 .integer_value = src_id, 2843 }); 2844 return result_id; 2845 } 2846 2847 // We can only use OpBitcast for specific conversions: between numerical types, and 2848 // between pointers. If the resolved spir-v types fall into this category then emit OpBitcast, 2849 // otherwise use a temporary and perform a pointer cast. 2850 const src_key = self.spv.cache.lookup(src_ty_ref); 2851 const dst_key = self.spv.cache.lookup(dst_ty_ref); 2852 2853 if ((src_key.isNumericalType() and dst_key.isNumericalType()) or (src_key == .ptr_type and dst_key == .ptr_type)) { 2854 const result_id = self.spv.allocId(); 2855 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 2856 .id_result_type = self.typeId(dst_ty_ref), 2857 .id_result = result_id, 2858 .operand = src_id, 2859 }); 2860 return result_id; 2861 } 2862 2863 const src_ptr_ty_ref = try self.spv.ptrType(src_ty_ref, .Function); 2864 const dst_ptr_ty_ref = try self.spv.ptrType(dst_ty_ref, .Function); 2865 2866 const tmp_id = self.spv.allocId(); 2867 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ 2868 .id_result_type = self.typeId(src_ptr_ty_ref), 2869 .id_result = tmp_id, 2870 .storage_class = .Function, 2871 }); 2872 try self.store(src_ty, tmp_id, src_id, false); 2873 const casted_ptr_id = self.spv.allocId(); 2874 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 2875 .id_result_type = self.typeId(dst_ptr_ty_ref), 2876 .id_result = casted_ptr_id, 2877 .operand = tmp_id, 2878 }); 2879 return try self.load(dst_ty, casted_ptr_id, false); 2880 } 2881 2882 fn airBitCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2883 if (self.liveness.isUnused(inst)) return null; 2884 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2885 const operand_id = try self.resolve(ty_op.operand); 2886 const operand_ty = self.typeOf(ty_op.operand); 2887 const result_ty = self.typeOfIndex(inst); 2888 return try self.bitCast(result_ty, operand_ty, operand_id); 2889 } 2890 2891 fn airIntCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2892 if (self.liveness.isUnused(inst)) return null; 2893 2894 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2895 const operand_id = try self.resolve(ty_op.operand); 2896 const src_ty = self.typeOf(ty_op.operand); 2897 const dst_ty = self.typeOfIndex(inst); 2898 const src_ty_ref = try self.resolveType(src_ty, .direct); 2899 const dst_ty_ref = try self.resolveType(dst_ty, .direct); 2900 2901 const src_info = try self.arithmeticTypeInfo(src_ty); 2902 const dst_info = try self.arithmeticTypeInfo(dst_ty); 2903 2904 // While intcast promises that the value already fits, the upper bits of a 2905 // strange integer may contain garbage. Therefore, mask/sign extend it before. 2906 const src_id = try self.normalizeInt(src_ty_ref, operand_id, src_info); 2907 2908 if (src_info.backing_bits == dst_info.backing_bits) { 2909 return src_id; 2910 } 2911 2912 const result_id = self.spv.allocId(); 2913 switch (dst_info.signedness) { 2914 .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ 2915 .id_result_type = self.typeId(dst_ty_ref), 2916 .id_result = result_id, 2917 .signed_value = src_id, 2918 }), 2919 .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ 2920 .id_result_type = self.typeId(dst_ty_ref), 2921 .id_result = result_id, 2922 .unsigned_value = src_id, 2923 }), 2924 } 2925 return result_id; 2926 } 2927 2928 fn intFromPtr(self: *DeclGen, operand_id: IdRef) !IdRef { 2929 const result_type_id = try self.resolveTypeId(Type.usize); 2930 const result_id = self.spv.allocId(); 2931 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ 2932 .id_result_type = result_type_id, 2933 .id_result = result_id, 2934 .pointer = operand_id, 2935 }); 2936 return result_id; 2937 } 2938 2939 fn airIntFromPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2940 if (self.liveness.isUnused(inst)) return null; 2941 2942 const un_op = self.air.instructions.items(.data)[inst].un_op; 2943 const operand_id = try self.resolve(un_op); 2944 return try self.intFromPtr(operand_id); 2945 } 2946 2947 fn airFloatFromInt(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2948 if (self.liveness.isUnused(inst)) return null; 2949 2950 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2951 const operand_ty = self.typeOf(ty_op.operand); 2952 const operand_id = try self.resolve(ty_op.operand); 2953 const operand_info = try self.arithmeticTypeInfo(operand_ty); 2954 const dest_ty = self.typeOfIndex(inst); 2955 const dest_ty_id = try self.resolveTypeId(dest_ty); 2956 2957 const result_id = self.spv.allocId(); 2958 switch (operand_info.signedness) { 2959 .signed => try self.func.body.emit(self.spv.gpa, .OpConvertSToF, .{ 2960 .id_result_type = dest_ty_id, 2961 .id_result = result_id, 2962 .signed_value = operand_id, 2963 }), 2964 .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertUToF, .{ 2965 .id_result_type = dest_ty_id, 2966 .id_result = result_id, 2967 .unsigned_value = operand_id, 2968 }), 2969 } 2970 return result_id; 2971 } 2972 2973 fn airIntFromFloat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2974 if (self.liveness.isUnused(inst)) return null; 2975 2976 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2977 const operand_id = try self.resolve(ty_op.operand); 2978 const dest_ty = self.typeOfIndex(inst); 2979 const dest_info = try self.arithmeticTypeInfo(dest_ty); 2980 const dest_ty_id = try self.resolveTypeId(dest_ty); 2981 2982 const result_id = self.spv.allocId(); 2983 switch (dest_info.signedness) { 2984 .signed => try self.func.body.emit(self.spv.gpa, .OpConvertFToS, .{ 2985 .id_result_type = dest_ty_id, 2986 .id_result = result_id, 2987 .float_value = operand_id, 2988 }), 2989 .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertFToU, .{ 2990 .id_result_type = dest_ty_id, 2991 .id_result = result_id, 2992 .float_value = operand_id, 2993 }), 2994 } 2995 return result_id; 2996 } 2997 2998 fn airFloatCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 2999 if (self.liveness.isUnused(inst)) return null; 3000 3001 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3002 const operand_id = try self.resolve(ty_op.operand); 3003 const dest_ty = self.typeOfIndex(inst); 3004 const dest_ty_id = try self.resolveTypeId(dest_ty); 3005 3006 const result_id = self.spv.allocId(); 3007 try self.func.body.emit(self.spv.gpa, .OpFConvert, .{ 3008 .id_result_type = dest_ty_id, 3009 .id_result = result_id, 3010 .float_value = operand_id, 3011 }); 3012 return result_id; 3013 } 3014 3015 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3016 if (self.liveness.isUnused(inst)) return null; 3017 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3018 const operand_id = try self.resolve(ty_op.operand); 3019 const result_ty = self.typeOfIndex(inst); 3020 const result_ty_id = try self.resolveTypeId(result_ty); 3021 const info = try self.arithmeticTypeInfo(result_ty); 3022 3023 const result_id = self.spv.allocId(); 3024 switch (info.class) { 3025 .bool => { 3026 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{ 3027 .id_result_type = result_ty_id, 3028 .id_result = result_id, 3029 .operand = operand_id, 3030 }); 3031 }, 3032 .float => unreachable, 3033 .composite_integer => unreachable, // TODO 3034 .strange_integer, .integer => { 3035 // Note: strange integer bits will be masked before operations that do not hold under modulo. 3036 try self.func.body.emit(self.spv.gpa, .OpNot, .{ 3037 .id_result_type = result_ty_id, 3038 .id_result = result_id, 3039 .operand = operand_id, 3040 }); 3041 }, 3042 } 3043 3044 return result_id; 3045 } 3046 3047 fn airArrayToSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3048 if (self.liveness.isUnused(inst)) return null; 3049 3050 const mod = self.module; 3051 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3052 const array_ptr_ty = self.typeOf(ty_op.operand); 3053 const array_ty = array_ptr_ty.childType(mod); 3054 const slice_ty = self.typeOfIndex(inst); 3055 const elem_ptr_ty = slice_ty.slicePtrFieldType(mod); 3056 3057 const elem_ptr_ty_ref = try self.resolveType(elem_ptr_ty, .direct); 3058 const slice_ty_ref = try self.resolveType(slice_ty, .direct); 3059 const size_ty_ref = try self.sizeType(); 3060 3061 const array_ptr_id = try self.resolve(ty_op.operand); 3062 const len_id = try self.constInt(size_ty_ref, array_ty.arrayLen(mod)); 3063 3064 const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(mod)) 3065 // Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type. 3066 try self.bitCast(elem_ptr_ty, array_ptr_ty, array_ptr_id) 3067 else 3068 // Convert the pointer-to-array to a pointer to the first element. 3069 try self.accessChain(elem_ptr_ty_ref, array_ptr_id, &.{0}); 3070 3071 return try self.constructStruct(slice_ty_ref, &.{ elem_ptr_id, len_id }); 3072 } 3073 3074 fn airSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3075 if (self.liveness.isUnused(inst)) return null; 3076 3077 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3078 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 3079 const ptr_id = try self.resolve(bin_op.lhs); 3080 const len_id = try self.resolve(bin_op.rhs); 3081 const slice_ty = self.typeOfIndex(inst); 3082 const slice_ty_ref = try self.resolveType(slice_ty, .direct); 3083 3084 return try self.constructStruct(slice_ty_ref, &.{ 3085 ptr_id, // Note: Type should not need to be converted to direct. 3086 len_id, // Note: Type should not need to be converted to direct. 3087 }); 3088 } 3089 3090 fn airAggregateInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3091 if (self.liveness.isUnused(inst)) return null; 3092 3093 const mod = self.module; 3094 const ip = &mod.intern_pool; 3095 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3096 const result_ty = self.typeOfIndex(inst); 3097 const result_ty_ref = try self.resolveType(result_ty, .direct); 3098 const len: usize = @intCast(result_ty.arrayLen(mod)); 3099 const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]); 3100 3101 switch (result_ty.zigTypeTag(mod)) { 3102 .Vector => unreachable, // TODO 3103 .Struct => { 3104 if (mod.typeToPackedStruct(result_ty)) |struct_type| { 3105 _ = struct_type; 3106 unreachable; // TODO 3107 } 3108 3109 const constituents = try self.gpa.alloc(IdRef, elements.len); 3110 defer self.gpa.free(constituents); 3111 var index: usize = 0; 3112 3113 switch (ip.indexToKey(result_ty.toIntern())) { 3114 .anon_struct_type => |tuple| { 3115 for (tuple.types.get(ip), elements, 0..) |field_ty, element, i| { 3116 if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; 3117 assert(field_ty.toType().hasRuntimeBits(mod)); 3118 3119 const id = try self.resolve(element); 3120 constituents[index] = try self.convertToIndirect(field_ty.toType(), id); 3121 index += 1; 3122 } 3123 }, 3124 .struct_type => |struct_type| { 3125 var it = struct_type.iterateRuntimeOrder(ip); 3126 for (elements, 0..) |element, i| { 3127 const field_index = it.next().?; 3128 if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; 3129 const field_ty = struct_type.field_types.get(ip)[field_index].toType(); 3130 assert(field_ty.hasRuntimeBitsIgnoreComptime(mod)); 3131 3132 const id = try self.resolve(element); 3133 constituents[index] = try self.convertToIndirect(field_ty, id); 3134 index += 1; 3135 } 3136 }, 3137 else => unreachable, 3138 } 3139 3140 return try self.constructStruct(result_ty_ref, constituents[0..index]); 3141 }, 3142 .Array => { 3143 const array_info = result_ty.arrayInfo(mod); 3144 const n_elems: usize = @intCast(result_ty.arrayLenIncludingSentinel(mod)); 3145 const elem_ids = try self.gpa.alloc(IdRef, n_elems); 3146 defer self.gpa.free(elem_ids); 3147 3148 for (elements, 0..) |element, i| { 3149 const id = try self.resolve(element); 3150 elem_ids[i] = try self.convertToIndirect(array_info.elem_type, id); 3151 } 3152 3153 if (array_info.sentinel) |sentinel_val| { 3154 elem_ids[n_elems - 1] = try self.constant(array_info.elem_type, sentinel_val, .indirect); 3155 } 3156 3157 return try self.constructArray(result_ty_ref, elem_ids); 3158 }, 3159 else => unreachable, 3160 } 3161 } 3162 3163 fn sliceOrArrayLen(self: *DeclGen, operand_id: IdRef, ty: Type) !IdRef { 3164 const mod = self.module; 3165 switch (ty.ptrSize(mod)) { 3166 .Slice => return self.extractField(Type.usize, operand_id, 1), 3167 .One => { 3168 const array_ty = ty.childType(mod); 3169 const elem_ty = array_ty.childType(mod); 3170 const abi_size = elem_ty.abiSize(mod); 3171 const usize_ty_ref = try self.resolveType(Type.usize, .direct); 3172 return self.spv.constInt(usize_ty_ref, array_ty.arrayLenIncludingSentinel(mod) * abi_size); 3173 }, 3174 .Many, .C => unreachable, 3175 } 3176 } 3177 3178 fn sliceOrArrayPtr(self: *DeclGen, operand_id: IdRef, ty: Type) !IdRef { 3179 const mod = self.module; 3180 if (ty.isSlice(mod)) { 3181 const ptr_ty = ty.slicePtrFieldType(mod); 3182 return self.extractField(ptr_ty, operand_id, 0); 3183 } 3184 return operand_id; 3185 } 3186 3187 fn airMemcpy(self: *DeclGen, inst: Air.Inst.Index) !void { 3188 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3189 const dest_slice = try self.resolve(bin_op.lhs); 3190 const src_slice = try self.resolve(bin_op.rhs); 3191 const dest_ty = self.typeOf(bin_op.lhs); 3192 const src_ty = self.typeOf(bin_op.rhs); 3193 const dest_ptr = try self.sliceOrArrayPtr(dest_slice, dest_ty); 3194 const src_ptr = try self.sliceOrArrayPtr(src_slice, src_ty); 3195 const len = try self.sliceOrArrayLen(dest_slice, dest_ty); 3196 try self.func.body.emit(self.spv.gpa, .OpCopyMemorySized, .{ 3197 .target = dest_ptr, 3198 .source = src_ptr, 3199 .size = len, 3200 }); 3201 } 3202 3203 fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef { 3204 if (self.liveness.isUnused(inst)) return null; 3205 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3206 const field_ty = self.typeOfIndex(inst); 3207 const operand_id = try self.resolve(ty_op.operand); 3208 return try self.extractField(field_ty, operand_id, field); 3209 } 3210 3211 fn airSliceElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3212 const mod = self.module; 3213 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3214 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 3215 const slice_ty = self.typeOf(bin_op.lhs); 3216 if (!slice_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null; 3217 3218 const slice_id = try self.resolve(bin_op.lhs); 3219 const index_id = try self.resolve(bin_op.rhs); 3220 3221 const ptr_ty = self.typeOfIndex(inst); 3222 const ptr_ty_ref = try self.resolveType(ptr_ty, .direct); 3223 3224 const slice_ptr = try self.extractField(ptr_ty, slice_id, 0); 3225 return try self.ptrAccessChain(ptr_ty_ref, slice_ptr, index_id, &.{}); 3226 } 3227 3228 fn airSliceElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3229 const mod = self.module; 3230 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3231 const slice_ty = self.typeOf(bin_op.lhs); 3232 if (!slice_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null; 3233 3234 const slice_id = try self.resolve(bin_op.lhs); 3235 const index_id = try self.resolve(bin_op.rhs); 3236 3237 const ptr_ty = slice_ty.slicePtrFieldType(mod); 3238 const ptr_ty_ref = try self.resolveType(ptr_ty, .direct); 3239 3240 const slice_ptr = try self.extractField(ptr_ty, slice_id, 0); 3241 const elem_ptr = try self.ptrAccessChain(ptr_ty_ref, slice_ptr, index_id, &.{}); 3242 return try self.load(slice_ty.childType(mod), elem_ptr, slice_ty.isVolatilePtr(mod)); 3243 } 3244 3245 fn ptrElemPtr(self: *DeclGen, ptr_ty: Type, ptr_id: IdRef, index_id: IdRef) !IdRef { 3246 const mod = self.module; 3247 // Construct new pointer type for the resulting pointer 3248 const elem_ty = ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T. 3249 const elem_ty_ref = try self.resolveType(elem_ty, .direct); 3250 const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(ptr_ty.ptrAddressSpace(mod))); 3251 if (ptr_ty.isSinglePointer(mod)) { 3252 // Pointer-to-array. In this case, the resulting pointer is not of the same type 3253 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain. 3254 return try self.accessChainId(elem_ptr_ty_ref, ptr_id, &.{index_id}); 3255 } else { 3256 // Resulting pointer type is the same as the ptr_ty, so use ptrAccessChain 3257 return try self.ptrAccessChain(elem_ptr_ty_ref, ptr_id, index_id, &.{}); 3258 } 3259 } 3260 3261 fn airPtrElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3262 if (self.liveness.isUnused(inst)) return null; 3263 3264 const mod = self.module; 3265 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3266 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 3267 const src_ptr_ty = self.typeOf(bin_op.lhs); 3268 const elem_ty = src_ptr_ty.childType(mod); 3269 const ptr_id = try self.resolve(bin_op.lhs); 3270 3271 if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) { 3272 const dst_ptr_ty = self.typeOfIndex(inst); 3273 return try self.bitCast(dst_ptr_ty, src_ptr_ty, ptr_id); 3274 } 3275 3276 const index_id = try self.resolve(bin_op.rhs); 3277 return try self.ptrElemPtr(src_ptr_ty, ptr_id, index_id); 3278 } 3279 3280 fn airArrayElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3281 if (self.liveness.isUnused(inst)) return null; 3282 3283 const mod = self.module; 3284 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3285 const array_ty = self.typeOf(bin_op.lhs); 3286 const array_ty_ref = try self.resolveType(array_ty, .direct); 3287 const elem_ty = array_ty.childType(mod); 3288 const elem_ty_ref = try self.resolveType(elem_ty, .indirect); 3289 const array_id = try self.resolve(bin_op.lhs); 3290 const index_id = try self.resolve(bin_op.rhs); 3291 3292 // SPIR-V doesn't have an array indexing function for some damn reason. 3293 // For now, just generate a temporary and use that. 3294 // TODO: This backend probably also should use isByRef from llvm... 3295 3296 const array_ptr_ty_ref = try self.spv.ptrType(array_ty_ref, .Function); 3297 const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, .Function); 3298 3299 const tmp_id = self.spv.allocId(); 3300 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ 3301 .id_result_type = self.typeId(array_ptr_ty_ref), 3302 .id_result = tmp_id, 3303 .storage_class = .Function, 3304 }); 3305 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 3306 .pointer = tmp_id, 3307 .object = array_id, 3308 }); 3309 3310 const elem_ptr_id = try self.accessChainId(elem_ptr_ty_ref, tmp_id, &.{index_id}); 3311 return try self.load(elem_ty, elem_ptr_id, false); 3312 } 3313 3314 fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3315 if (self.liveness.isUnused(inst)) return null; 3316 3317 const mod = self.module; 3318 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3319 const ptr_ty = self.typeOf(bin_op.lhs); 3320 const elem_ty = self.typeOfIndex(inst); 3321 const ptr_id = try self.resolve(bin_op.lhs); 3322 const index_id = try self.resolve(bin_op.rhs); 3323 const elem_ptr_id = try self.ptrElemPtr(ptr_ty, ptr_id, index_id); 3324 return try self.load(elem_ty, elem_ptr_id, ptr_ty.isVolatilePtr(mod)); 3325 } 3326 3327 fn airSetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !void { 3328 const mod = self.module; 3329 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3330 const un_ptr_ty = self.typeOf(bin_op.lhs); 3331 const un_ty = un_ptr_ty.childType(mod); 3332 const layout = self.unionLayout(un_ty, null); 3333 3334 if (layout.tag_size == 0) return; 3335 3336 const tag_ty = un_ty.unionTagTypeSafety(mod).?; 3337 const tag_ty_ref = try self.resolveType(tag_ty, .indirect); 3338 const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, spvStorageClass(un_ptr_ty.ptrAddressSpace(mod))); 3339 3340 const union_ptr_id = try self.resolve(bin_op.lhs); 3341 const new_tag_id = try self.resolve(bin_op.rhs); 3342 3343 if (layout.payload_size == 0) { 3344 try self.store(tag_ty, union_ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod)); 3345 } else { 3346 const ptr_id = try self.accessChain(tag_ptr_ty_ref, union_ptr_id, &.{layout.tag_index}); 3347 try self.store(tag_ty, ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod)); 3348 } 3349 } 3350 3351 fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3352 if (self.liveness.isUnused(inst)) return null; 3353 3354 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3355 const un_ty = self.typeOf(ty_op.operand); 3356 3357 const mod = self.module; 3358 const layout = self.unionLayout(un_ty, null); 3359 if (layout.tag_size == 0) return null; 3360 3361 const union_handle = try self.resolve(ty_op.operand); 3362 if (layout.payload_size == 0) return union_handle; 3363 3364 const tag_ty = un_ty.unionTagTypeSafety(mod).?; 3365 return try self.extractField(tag_ty, union_handle, layout.tag_index); 3366 } 3367 3368 fn unionInit( 3369 self: *DeclGen, 3370 ty: Type, 3371 active_field: u32, 3372 payload: ?IdRef, 3373 ) !IdRef { 3374 // To initialize a union, generate a temporary variable with the 3375 // type that has the right field active, then pointer-cast and store 3376 // the active field, and finally load and return the entire union. 3377 3378 const mod = self.module; 3379 const ip = &mod.intern_pool; 3380 const union_ty = mod.typeToUnion(ty).?; 3381 3382 if (union_ty.getLayout(ip) == .Packed) { 3383 unreachable; // TODO 3384 } 3385 3386 const maybe_tag_ty = ty.unionTagTypeSafety(mod); 3387 const layout = self.unionLayout(ty, active_field); 3388 3389 const tag_int = if (layout.tag_size != 0) blk: { 3390 const tag_ty = maybe_tag_ty.?; 3391 const union_field_name = union_ty.field_names.get(ip)[active_field]; 3392 const enum_field_index = tag_ty.enumFieldIndex(union_field_name, mod).?; 3393 const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); 3394 const tag_int_val = try tag_val.intFromEnum(tag_ty, mod); 3395 break :blk tag_int_val.toUnsignedInt(mod); 3396 } else 0; 3397 3398 if (layout.payload_size == 0) { 3399 const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct); 3400 return try self.constInt(tag_ty_ref, tag_int); 3401 } 3402 3403 const un_active_ty_ref = try self.resolveUnionType(ty, active_field); 3404 const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function); 3405 const un_general_ty_ref = try self.resolveType(ty, .direct); 3406 const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function); 3407 3408 const tmp_id = self.spv.allocId(); 3409 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ 3410 .id_result_type = self.typeId(un_active_ptr_ty_ref), 3411 .id_result = tmp_id, 3412 .storage_class = .Function, 3413 }); 3414 3415 if (layout.tag_size != 0) { 3416 const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct); 3417 const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function); 3418 const ptr_id = try self.accessChain(tag_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.tag_index))}); 3419 const tag_id = try self.constInt(tag_ty_ref, tag_int); 3420 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 3421 .pointer = ptr_id, 3422 .object = tag_id, 3423 }); 3424 } 3425 3426 if (layout.active_field_size != 0) { 3427 const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect); 3428 const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function); 3429 const ptr_id = try self.accessChain(active_field_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.active_field_index))}); 3430 try self.func.body.emit(self.spv.gpa, .OpStore, .{ 3431 .pointer = ptr_id, 3432 .object = payload.?, 3433 }); 3434 } else { 3435 assert(payload == null); 3436 } 3437 3438 // Just leave the padding fields uninitialized... 3439 // TODO: Or should we initialize them with undef explicitly? 3440 3441 // Now cast the pointer and load it as the 'generic' union type. 3442 3443 const casted_var_id = self.spv.allocId(); 3444 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 3445 .id_result_type = self.typeId(un_general_ptr_ty_ref), 3446 .id_result = casted_var_id, 3447 .operand = tmp_id, 3448 }); 3449 3450 const result_id = self.spv.allocId(); 3451 try self.func.body.emit(self.spv.gpa, .OpLoad, .{ 3452 .id_result_type = self.typeId(un_general_ty_ref), 3453 .id_result = result_id, 3454 .pointer = casted_var_id, 3455 }); 3456 3457 return result_id; 3458 } 3459 3460 fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3461 if (self.liveness.isUnused(inst)) return null; 3462 3463 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3464 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; 3465 const ty = self.typeOfIndex(inst); 3466 const layout = self.unionLayout(ty, extra.field_index); 3467 3468 const payload = if (layout.active_field_size != 0) 3469 try self.resolve(extra.init) 3470 else 3471 null; 3472 return try self.unionInit(ty, extra.field_index, payload); 3473 } 3474 3475 fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3476 if (self.liveness.isUnused(inst)) return null; 3477 3478 const mod = self.module; 3479 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3480 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; 3481 3482 const object_ty = self.typeOf(struct_field.struct_operand); 3483 const object_id = try self.resolve(struct_field.struct_operand); 3484 const field_index = struct_field.field_index; 3485 const field_ty = object_ty.structFieldType(field_index, mod); 3486 3487 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) return null; 3488 3489 switch (object_ty.zigTypeTag(mod)) { 3490 .Struct => switch (object_ty.containerLayout(mod)) { 3491 .Packed => unreachable, // TODO 3492 else => return try self.extractField(field_ty, object_id, field_index), 3493 }, 3494 .Union => switch (object_ty.containerLayout(mod)) { 3495 .Packed => unreachable, // TODO 3496 else => { 3497 // Store, pointer-cast, load 3498 const un_general_ty_ref = try self.resolveType(object_ty, .indirect); 3499 const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function); 3500 const un_active_ty_ref = try self.resolveUnionType(object_ty, field_index); 3501 const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function); 3502 const field_ty_ref = try self.resolveType(field_ty, .indirect); 3503 const field_ptr_ty_ref = try self.spv.ptrType(field_ty_ref, .Function); 3504 3505 const tmp_id = self.spv.allocId(); 3506 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ 3507 .id_result_type = self.typeId(un_general_ptr_ty_ref), 3508 .id_result = tmp_id, 3509 .storage_class = .Function, 3510 }); 3511 try self.store(object_ty, tmp_id, object_id, false); 3512 const casted_tmp_id = self.spv.allocId(); 3513 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 3514 .id_result_type = self.typeId(un_active_ptr_ty_ref), 3515 .id_result = casted_tmp_id, 3516 .operand = tmp_id, 3517 }); 3518 const layout = self.unionLayout(object_ty, field_index); 3519 const field_ptr_id = try self.accessChain(field_ptr_ty_ref, casted_tmp_id, &.{layout.active_field_index}); 3520 return try self.load(field_ty, field_ptr_id, false); 3521 }, 3522 }, 3523 else => unreachable, 3524 } 3525 } 3526 3527 fn airFieldParentPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3528 const mod = self.module; 3529 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3530 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; 3531 3532 const parent_ty = self.air.getRefType(ty_pl.ty).childType(mod); 3533 const res_ty = try self.resolveType(self.air.getRefType(ty_pl.ty), .indirect); 3534 const usize_ty = Type.usize; 3535 const usize_ty_ref = try self.resolveType(usize_ty, .direct); 3536 3537 const field_ptr = try self.resolve(extra.field_ptr); 3538 const field_ptr_int = try self.intFromPtr(field_ptr); 3539 const field_offset = parent_ty.structFieldOffset(extra.field_index, mod); 3540 3541 const base_ptr_int = base_ptr_int: { 3542 if (field_offset == 0) break :base_ptr_int field_ptr_int; 3543 3544 const field_offset_id = try self.constInt(usize_ty_ref, field_offset); 3545 break :base_ptr_int try self.binOpSimple(usize_ty, field_ptr_int, field_offset_id, .OpISub); 3546 }; 3547 3548 const base_ptr = self.spv.allocId(); 3549 try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{ 3550 .id_result_type = self.spv.resultId(res_ty), 3551 .id_result = base_ptr, 3552 .integer_value = base_ptr_int, 3553 }); 3554 3555 return base_ptr; 3556 } 3557 3558 fn structFieldPtr( 3559 self: *DeclGen, 3560 result_ptr_ty: Type, 3561 object_ptr_ty: Type, 3562 object_ptr: IdRef, 3563 field_index: u32, 3564 ) !IdRef { 3565 const result_ty_ref = try self.resolveType(result_ptr_ty, .direct); 3566 3567 const mod = self.module; 3568 const object_ty = object_ptr_ty.childType(mod); 3569 switch (object_ty.zigTypeTag(mod)) { 3570 .Struct => switch (object_ty.containerLayout(mod)) { 3571 .Packed => unreachable, // TODO 3572 else => { 3573 return try self.accessChain(result_ty_ref, object_ptr, &.{field_index}); 3574 }, 3575 }, 3576 .Union => switch (object_ty.containerLayout(mod)) { 3577 .Packed => unreachable, // TODO 3578 else => { 3579 const storage_class = spvStorageClass(object_ptr_ty.ptrAddressSpace(mod)); 3580 const un_active_ty_ref = try self.resolveUnionType(object_ty, field_index); 3581 const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, storage_class); 3582 3583 const casted_id = self.spv.allocId(); 3584 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ 3585 .id_result_type = self.typeId(un_active_ptr_ty_ref), 3586 .id_result = casted_id, 3587 .operand = object_ptr, 3588 }); 3589 const layout = self.unionLayout(object_ty, field_index); 3590 return try self.accessChain(result_ty_ref, casted_id, &.{layout.active_field_index}); 3591 }, 3592 }, 3593 else => unreachable, 3594 } 3595 } 3596 3597 fn airStructFieldPtrIndex(self: *DeclGen, inst: Air.Inst.Index, field_index: u32) !?IdRef { 3598 if (self.liveness.isUnused(inst)) return null; 3599 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3600 const struct_ptr = try self.resolve(ty_op.operand); 3601 const struct_ptr_ty = self.typeOf(ty_op.operand); 3602 const result_ptr_ty = self.typeOfIndex(inst); 3603 return try self.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index); 3604 } 3605 3606 /// We cannot use an OpVariable directly in an OpSpecConstantOp, but we can 3607 /// after we insert a dummy AccessChain... 3608 /// TODO: Get rid of this 3609 fn makePointerConstant( 3610 self: *DeclGen, 3611 section: *SpvSection, 3612 ptr_ty_ref: CacheRef, 3613 ptr_id: IdRef, 3614 ) !IdRef { 3615 const result_id = self.spv.allocId(); 3616 try section.emitSpecConstantOp(self.spv.gpa, .OpInBoundsAccessChain, .{ 3617 .id_result_type = self.typeId(ptr_ty_ref), 3618 .id_result = result_id, 3619 .base = ptr_id, 3620 }); 3621 return result_id; 3622 } 3623 3624 // Allocate a function-local variable, with possible initializer. 3625 // This function returns a pointer to a variable of type `ty_ref`, 3626 // which is in the Generic address space. The variable is actually 3627 // placed in the Function address space. 3628 fn alloc( 3629 self: *DeclGen, 3630 ty_ref: CacheRef, 3631 initializer: ?IdRef, 3632 ) !IdRef { 3633 const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function); 3634 const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic); 3635 3636 // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to 3637 // directly generate them into func.prologue instead of the body. 3638 const var_id = self.spv.allocId(); 3639 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ 3640 .id_result_type = self.typeId(fn_ptr_ty_ref), 3641 .id_result = var_id, 3642 .storage_class = .Function, 3643 .initializer = initializer, 3644 }); 3645 3646 // Convert to a generic pointer 3647 const result_id = self.spv.allocId(); 3648 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ 3649 .id_result_type = self.typeId(general_ptr_ty_ref), 3650 .id_result = result_id, 3651 .pointer = var_id, 3652 }); 3653 return result_id; 3654 } 3655 3656 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3657 if (self.liveness.isUnused(inst)) return null; 3658 const mod = self.module; 3659 const ptr_ty = self.typeOfIndex(inst); 3660 assert(ptr_ty.ptrAddressSpace(mod) == .generic); 3661 const child_ty = ptr_ty.childType(mod); 3662 const child_ty_ref = try self.resolveType(child_ty, .indirect); 3663 return try self.alloc(child_ty_ref, null); 3664 } 3665 3666 fn airArg(self: *DeclGen) IdRef { 3667 defer self.next_arg_index += 1; 3668 return self.args.items[self.next_arg_index]; 3669 } 3670 3671 fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3672 // In AIR, a block doesn't really define an entry point like a block, but 3673 // more like a scope that breaks can jump out of and "return" a value from. 3674 // This cannot be directly modelled in SPIR-V, so in a block instruction, 3675 // we're going to split up the current block by first generating the code 3676 // of the block, then a label, and then generate the rest of the current 3677 // ir.Block in a different SPIR-V block. 3678 3679 const mod = self.module; 3680 const ty = self.typeOfIndex(inst); 3681 const inst_datas = self.air.instructions.items(.data); 3682 const extra = self.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload); 3683 const body = self.air.extra[extra.end..][0..extra.data.body_len]; 3684 const have_block_result = ty.isFnOrHasRuntimeBitsIgnoreComptime(mod); 3685 3686 // 4 chosen as arbitrary initial capacity. 3687 var block = Block{ 3688 // Label id is lazily allocated if needed. 3689 .label_id = null, 3690 .incoming_blocks = try std.ArrayListUnmanaged(IncomingBlock).initCapacity(self.gpa, 4), 3691 }; 3692 defer block.incoming_blocks.deinit(self.gpa); 3693 3694 try self.blocks.putNoClobber(self.gpa, inst, &block); 3695 defer assert(self.blocks.remove(inst)); 3696 3697 try self.genBody(body); 3698 3699 // Only begin a new block if there were actually any breaks towards it. 3700 if (block.label_id) |label_id| { 3701 try self.beginSpvBlock(label_id); 3702 } 3703 3704 if (!have_block_result) 3705 return null; 3706 3707 assert(block.label_id != null); 3708 const result_id = self.spv.allocId(); 3709 const result_type_id = try self.resolveTypeId(ty); 3710 3711 try self.func.body.emitRaw(self.spv.gpa, .OpPhi, 2 + @as(u16, @intCast(block.incoming_blocks.items.len * 2))); // result type + result + variable/parent... 3712 self.func.body.writeOperand(spec.IdResultType, result_type_id); 3713 self.func.body.writeOperand(spec.IdRef, result_id); 3714 3715 for (block.incoming_blocks.items) |incoming| { 3716 self.func.body.writeOperand(spec.PairIdRefIdRef, .{ incoming.break_value_id, incoming.src_label_id }); 3717 } 3718 3719 return result_id; 3720 } 3721 3722 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void { 3723 const br = self.air.instructions.items(.data)[inst].br; 3724 const operand_ty = self.typeOf(br.operand); 3725 const block = self.blocks.get(br.block_inst).?; 3726 3727 const mod = self.module; 3728 if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { 3729 const operand_id = try self.resolve(br.operand); 3730 // current_block_label_id should not be undefined here, lest there is a br or br_void in the function's body. 3731 try block.incoming_blocks.append(self.gpa, .{ 3732 .src_label_id = self.current_block_label_id, 3733 .break_value_id = operand_id, 3734 }); 3735 } 3736 3737 if (block.label_id == null) { 3738 block.label_id = self.spv.allocId(); 3739 } 3740 3741 try self.func.body.emit(self.spv.gpa, .OpBranch, .{ .target_label = block.label_id.? }); 3742 } 3743 3744 fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void { 3745 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 3746 const cond_br = self.air.extraData(Air.CondBr, pl_op.payload); 3747 const then_body = self.air.extra[cond_br.end..][0..cond_br.data.then_body_len]; 3748 const else_body = self.air.extra[cond_br.end + then_body.len ..][0..cond_br.data.else_body_len]; 3749 const condition_id = try self.resolve(pl_op.operand); 3750 3751 // These will always generate a new SPIR-V block, since they are ir.Body and not ir.Block. 3752 const then_label_id = self.spv.allocId(); 3753 const else_label_id = self.spv.allocId(); 3754 3755 // TODO: We can generate OpSelectionMerge here if we know the target block that both of these will resolve to, 3756 // but i don't know if those will always resolve to the same block. 3757 3758 try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{ 3759 .condition = condition_id, 3760 .true_label = then_label_id, 3761 .false_label = else_label_id, 3762 }); 3763 3764 try self.beginSpvBlock(then_label_id); 3765 try self.genBody(then_body); 3766 try self.beginSpvBlock(else_label_id); 3767 try self.genBody(else_body); 3768 } 3769 3770 fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3771 const mod = self.module; 3772 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3773 const ptr_ty = self.typeOf(ty_op.operand); 3774 const elem_ty = self.typeOfIndex(inst); 3775 const operand = try self.resolve(ty_op.operand); 3776 if (!ptr_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null; 3777 3778 return try self.load(elem_ty, operand, ptr_ty.isVolatilePtr(mod)); 3779 } 3780 3781 fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void { 3782 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3783 const ptr_ty = self.typeOf(bin_op.lhs); 3784 const elem_ty = ptr_ty.childType(self.module); 3785 const ptr = try self.resolve(bin_op.lhs); 3786 const value = try self.resolve(bin_op.rhs); 3787 3788 try self.store(elem_ty, ptr, value, ptr_ty.isVolatilePtr(self.module)); 3789 } 3790 3791 fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void { 3792 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3793 const loop = self.air.extraData(Air.Block, ty_pl.payload); 3794 const body = self.air.extra[loop.end..][0..loop.data.body_len]; 3795 const loop_label_id = self.spv.allocId(); 3796 3797 // Jump to the loop entry point 3798 try self.func.body.emit(self.spv.gpa, .OpBranch, .{ .target_label = loop_label_id }); 3799 3800 // TODO: Look into OpLoopMerge. 3801 try self.beginSpvBlock(loop_label_id); 3802 try self.genBody(body); 3803 3804 try self.func.body.emit(self.spv.gpa, .OpBranch, .{ .target_label = loop_label_id }); 3805 } 3806 3807 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void { 3808 const operand = self.air.instructions.items(.data)[inst].un_op; 3809 const ret_ty = self.typeOf(operand); 3810 const mod = self.module; 3811 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) { 3812 const decl = mod.declPtr(self.decl_index); 3813 const fn_info = mod.typeToFunc(decl.ty).?; 3814 if (fn_info.return_type.toType().isError(mod)) { 3815 // Functions with an empty error set are emitted with an error code 3816 // return type and return zero so they can be function pointers coerced 3817 // to functions that return anyerror. 3818 const err_ty_ref = try self.resolveType(Type.anyerror, .direct); 3819 const no_err_id = try self.constInt(err_ty_ref, 0); 3820 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id }); 3821 } else { 3822 return try self.func.body.emit(self.spv.gpa, .OpReturn, {}); 3823 } 3824 } 3825 3826 const operand_id = try self.resolve(operand); 3827 try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = operand_id }); 3828 } 3829 3830 fn airRetLoad(self: *DeclGen, inst: Air.Inst.Index) !void { 3831 const mod = self.module; 3832 const un_op = self.air.instructions.items(.data)[inst].un_op; 3833 const ptr_ty = self.typeOf(un_op); 3834 const ret_ty = ptr_ty.childType(mod); 3835 3836 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) { 3837 const decl = mod.declPtr(self.decl_index); 3838 const fn_info = mod.typeToFunc(decl.ty).?; 3839 if (fn_info.return_type.toType().isError(mod)) { 3840 // Functions with an empty error set are emitted with an error code 3841 // return type and return zero so they can be function pointers coerced 3842 // to functions that return anyerror. 3843 const err_ty_ref = try self.resolveType(Type.anyerror, .direct); 3844 const no_err_id = try self.constInt(err_ty_ref, 0); 3845 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id }); 3846 } else { 3847 return try self.func.body.emit(self.spv.gpa, .OpReturn, {}); 3848 } 3849 } 3850 3851 const ptr = try self.resolve(un_op); 3852 const value = try self.load(ret_ty, ptr, ptr_ty.isVolatilePtr(mod)); 3853 try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ 3854 .value = value, 3855 }); 3856 } 3857 3858 fn airTry(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3859 const mod = self.module; 3860 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 3861 const err_union_id = try self.resolve(pl_op.operand); 3862 const extra = self.air.extraData(Air.Try, pl_op.payload); 3863 const body = self.air.extra[extra.end..][0..extra.data.body_len]; 3864 3865 const err_union_ty = self.typeOf(pl_op.operand); 3866 const payload_ty = self.typeOfIndex(inst); 3867 3868 const err_ty_ref = try self.resolveType(Type.anyerror, .direct); 3869 const bool_ty_ref = try self.resolveType(Type.bool, .direct); 3870 3871 const eu_layout = self.errorUnionLayout(payload_ty); 3872 3873 if (!err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { 3874 const err_id = if (eu_layout.payload_has_bits) 3875 try self.extractField(Type.anyerror, err_union_id, eu_layout.errorFieldIndex()) 3876 else 3877 err_union_id; 3878 3879 const zero_id = try self.constInt(err_ty_ref, 0); 3880 const is_err_id = self.spv.allocId(); 3881 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ 3882 .id_result_type = self.typeId(bool_ty_ref), 3883 .id_result = is_err_id, 3884 .operand_1 = err_id, 3885 .operand_2 = zero_id, 3886 }); 3887 3888 // When there is an error, we must evaluate `body`. Otherwise we must continue 3889 // with the current body. 3890 // Just generate a new block here, then generate a new block inline for the remainder of the body. 3891 3892 const err_block = self.spv.allocId(); 3893 const ok_block = self.spv.allocId(); 3894 3895 // TODO: Merge block 3896 try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{ 3897 .condition = is_err_id, 3898 .true_label = err_block, 3899 .false_label = ok_block, 3900 }); 3901 3902 try self.beginSpvBlock(err_block); 3903 try self.genBody(body); 3904 3905 try self.beginSpvBlock(ok_block); 3906 // Now just extract the payload, if required. 3907 } 3908 if (self.liveness.isUnused(inst)) { 3909 return null; 3910 } 3911 if (!eu_layout.payload_has_bits) { 3912 return null; 3913 } 3914 3915 return try self.extractField(payload_ty, err_union_id, eu_layout.payloadFieldIndex()); 3916 } 3917 3918 fn airErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3919 if (self.liveness.isUnused(inst)) return null; 3920 3921 const mod = self.module; 3922 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3923 const operand_id = try self.resolve(ty_op.operand); 3924 const err_union_ty = self.typeOf(ty_op.operand); 3925 const err_ty_ref = try self.resolveType(Type.anyerror, .direct); 3926 3927 if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { 3928 // No error possible, so just return undefined. 3929 return try self.spv.constUndef(err_ty_ref); 3930 } 3931 3932 const payload_ty = err_union_ty.errorUnionPayload(mod); 3933 const eu_layout = self.errorUnionLayout(payload_ty); 3934 3935 if (!eu_layout.payload_has_bits) { 3936 // If no payload, error union is represented by error set. 3937 return operand_id; 3938 } 3939 3940 return try self.extractField(Type.anyerror, operand_id, eu_layout.errorFieldIndex()); 3941 } 3942 3943 fn airErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3944 if (self.liveness.isUnused(inst)) return null; 3945 3946 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3947 const operand_id = try self.resolve(ty_op.operand); 3948 const payload_ty = self.typeOfIndex(inst); 3949 const eu_layout = self.errorUnionLayout(payload_ty); 3950 3951 if (!eu_layout.payload_has_bits) { 3952 return null; // No error possible. 3953 } 3954 3955 return try self.extractField(payload_ty, operand_id, eu_layout.payloadFieldIndex()); 3956 } 3957 3958 fn airWrapErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3959 if (self.liveness.isUnused(inst)) return null; 3960 3961 const mod = self.module; 3962 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3963 const err_union_ty = self.typeOfIndex(inst); 3964 const payload_ty = err_union_ty.errorUnionPayload(mod); 3965 const operand_id = try self.resolve(ty_op.operand); 3966 const eu_layout = self.errorUnionLayout(payload_ty); 3967 3968 if (!eu_layout.payload_has_bits) { 3969 return operand_id; 3970 } 3971 3972 const payload_ty_ref = try self.resolveType(payload_ty, .indirect); 3973 3974 var members: [2]IdRef = undefined; 3975 members[eu_layout.errorFieldIndex()] = operand_id; 3976 members[eu_layout.payloadFieldIndex()] = try self.spv.constUndef(payload_ty_ref); 3977 3978 const err_union_ty_ref = try self.resolveType(err_union_ty, .direct); 3979 return try self.constructStruct(err_union_ty_ref, &members); 3980 } 3981 3982 fn airWrapErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 3983 if (self.liveness.isUnused(inst)) return null; 3984 3985 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3986 const err_union_ty = self.typeOfIndex(inst); 3987 const operand_id = try self.resolve(ty_op.operand); 3988 const payload_ty = self.typeOf(ty_op.operand); 3989 const err_ty_ref = try self.resolveType(Type.anyerror, .direct); 3990 const eu_layout = self.errorUnionLayout(payload_ty); 3991 3992 if (!eu_layout.payload_has_bits) { 3993 return try self.constInt(err_ty_ref, 0); 3994 } 3995 3996 var members: [2]IdRef = undefined; 3997 members[eu_layout.errorFieldIndex()] = try self.constInt(err_ty_ref, 0); 3998 members[eu_layout.payloadFieldIndex()] = try self.convertToIndirect(payload_ty, operand_id); 3999 4000 const err_union_ty_ref = try self.resolveType(err_union_ty, .direct); 4001 return try self.constructStruct(err_union_ty_ref, &members); 4002 } 4003 4004 fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_null, is_non_null }) !?IdRef { 4005 if (self.liveness.isUnused(inst)) return null; 4006 4007 const mod = self.module; 4008 const un_op = self.air.instructions.items(.data)[inst].un_op; 4009 const operand_id = try self.resolve(un_op); 4010 const optional_ty = self.typeOf(un_op); 4011 4012 const payload_ty = optional_ty.optionalChild(mod); 4013 4014 const bool_ty_ref = try self.resolveType(Type.bool, .direct); 4015 4016 if (optional_ty.optionalReprIsPayload(mod)) { 4017 // Pointer payload represents nullability: pointer or slice. 4018 4019 const ptr_ty = if (payload_ty.isSlice(mod)) 4020 payload_ty.slicePtrFieldType(mod) 4021 else 4022 payload_ty; 4023 4024 const ptr_id = if (payload_ty.isSlice(mod)) 4025 try self.extractField(ptr_ty, operand_id, 0) 4026 else 4027 operand_id; 4028 4029 const payload_ty_ref = try self.resolveType(ptr_ty, .direct); 4030 const null_id = try self.spv.constNull(payload_ty_ref); 4031 const op: std.math.CompareOperator = switch (pred) { 4032 .is_null => .eq, 4033 .is_non_null => .neq, 4034 }; 4035 return try self.cmp(op, ptr_ty, ptr_id, null_id); 4036 } 4037 4038 const is_non_null_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) 4039 try self.extractField(Type.bool, operand_id, 1) 4040 else 4041 // Optional representation is bool indicating whether the optional is set 4042 // Optionals with no payload are represented as an (indirect) bool, so convert 4043 // it back to the direct bool here. 4044 try self.convertToDirect(Type.bool, operand_id); 4045 4046 return switch (pred) { 4047 .is_null => blk: { 4048 // Invert condition 4049 const result_id = self.spv.allocId(); 4050 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{ 4051 .id_result_type = self.typeId(bool_ty_ref), 4052 .id_result = result_id, 4053 .operand = is_non_null_id, 4054 }); 4055 break :blk result_id; 4056 }, 4057 .is_non_null => is_non_null_id, 4058 }; 4059 } 4060 4061 fn airIsErr(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_err, is_non_err }) !?IdRef { 4062 if (self.liveness.isUnused(inst)) return null; 4063 4064 const mod = self.module; 4065 const un_op = self.air.instructions.items(.data)[inst].un_op; 4066 const operand_id = try self.resolve(un_op); 4067 const err_union_ty = self.typeOf(un_op); 4068 4069 if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { 4070 return try self.constBool(pred == .is_non_err, .direct); 4071 } 4072 4073 const payload_ty = err_union_ty.errorUnionPayload(mod); 4074 const eu_layout = self.errorUnionLayout(payload_ty); 4075 const bool_ty_ref = try self.resolveType(Type.bool, .direct); 4076 const err_ty_ref = try self.resolveType(Type.anyerror, .direct); 4077 4078 const error_id = if (!eu_layout.payload_has_bits) 4079 operand_id 4080 else 4081 try self.extractField(Type.anyerror, operand_id, eu_layout.errorFieldIndex()); 4082 4083 const result_id = self.spv.allocId(); 4084 const operands = .{ 4085 .id_result_type = self.typeId(bool_ty_ref), 4086 .id_result = result_id, 4087 .operand_1 = error_id, 4088 .operand_2 = try self.constInt(err_ty_ref, 0), 4089 }; 4090 switch (pred) { 4091 .is_err => try self.func.body.emit(self.spv.gpa, .OpINotEqual, operands), 4092 .is_non_err => try self.func.body.emit(self.spv.gpa, .OpIEqual, operands), 4093 } 4094 return result_id; 4095 } 4096 4097 fn airUnwrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4098 if (self.liveness.isUnused(inst)) return null; 4099 4100 const mod = self.module; 4101 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 4102 const operand_id = try self.resolve(ty_op.operand); 4103 const optional_ty = self.typeOf(ty_op.operand); 4104 const payload_ty = self.typeOfIndex(inst); 4105 4106 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return null; 4107 4108 if (optional_ty.optionalReprIsPayload(mod)) { 4109 return operand_id; 4110 } 4111 4112 return try self.extractField(payload_ty, operand_id, 0); 4113 } 4114 4115 fn airWrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4116 if (self.liveness.isUnused(inst)) return null; 4117 4118 const mod = self.module; 4119 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 4120 const payload_ty = self.typeOf(ty_op.operand); 4121 4122 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { 4123 return try self.constBool(true, .indirect); 4124 } 4125 4126 const operand_id = try self.resolve(ty_op.operand); 4127 4128 const optional_ty = self.typeOfIndex(inst); 4129 if (optional_ty.optionalReprIsPayload(mod)) { 4130 return operand_id; 4131 } 4132 4133 const optional_ty_ref = try self.resolveType(optional_ty, .direct); 4134 const payload_id = try self.convertToIndirect(payload_ty, operand_id); 4135 const members = [_]IdRef{ payload_id, try self.constBool(true, .indirect) }; 4136 return try self.constructStruct(optional_ty_ref, &members); 4137 } 4138 4139 fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { 4140 const mod = self.module; 4141 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 4142 const cond_ty = self.typeOf(pl_op.operand); 4143 const cond = try self.resolve(pl_op.operand); 4144 const cond_indirect = try self.convertToIndirect(cond_ty, cond); 4145 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); 4146 4147 const cond_words: u32 = switch (cond_ty.zigTypeTag(mod)) { 4148 .Bool => 1, 4149 .Int => blk: { 4150 const bits = cond_ty.intInfo(mod).bits; 4151 const backing_bits = self.backingIntBits(bits) orelse { 4152 return self.todo("implement composite int switch", .{}); 4153 }; 4154 break :blk if (backing_bits <= 32) @as(u32, 1) else 2; 4155 }, 4156 .Enum => blk: { 4157 const int_ty = cond_ty.intTagType(mod); 4158 const int_info = int_ty.intInfo(mod); 4159 const backing_bits = self.backingIntBits(int_info.bits) orelse { 4160 return self.todo("implement composite int switch", .{}); 4161 }; 4162 break :blk if (backing_bits <= 32) @as(u32, 1) else 2; 4163 }, 4164 .ErrorSet => 1, 4165 else => return self.todo("implement switch for type {s}", .{@tagName(cond_ty.zigTypeTag(mod))}), // TODO: Figure out which types apply here, and work around them as we can only do integers. 4166 }; 4167 4168 const num_cases = switch_br.data.cases_len; 4169 4170 // Compute the total number of arms that we need. 4171 // Zig switches are grouped by condition, so we need to loop through all of them 4172 const num_conditions = blk: { 4173 var extra_index: usize = switch_br.end; 4174 var case_i: u32 = 0; 4175 var num_conditions: u32 = 0; 4176 while (case_i < num_cases) : (case_i += 1) { 4177 const case = self.air.extraData(Air.SwitchBr.Case, extra_index); 4178 const case_body = self.air.extra[case.end + case.data.items_len ..][0..case.data.body_len]; 4179 extra_index = case.end + case.data.items_len + case_body.len; 4180 num_conditions += case.data.items_len; 4181 } 4182 break :blk num_conditions; 4183 }; 4184 4185 // First, pre-allocate the labels for the cases. 4186 const first_case_label = self.spv.allocIds(num_cases); 4187 // We always need the default case - if zig has none, we will generate unreachable there. 4188 const default = self.spv.allocId(); 4189 4190 // Emit the instruction before generating the blocks. 4191 try self.func.body.emitRaw(self.spv.gpa, .OpSwitch, 2 + (cond_words + 1) * num_conditions); 4192 self.func.body.writeOperand(IdRef, cond_indirect); 4193 self.func.body.writeOperand(IdRef, default); 4194 4195 // Emit each of the cases 4196 { 4197 var extra_index: usize = switch_br.end; 4198 var case_i: u32 = 0; 4199 while (case_i < num_cases) : (case_i += 1) { 4200 // SPIR-V needs a literal here, which' width depends on the case condition. 4201 const case = self.air.extraData(Air.SwitchBr.Case, extra_index); 4202 const items = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[case.end..][0..case.data.items_len])); 4203 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; 4204 extra_index = case.end + case.data.items_len + case_body.len; 4205 4206 const label = IdRef{ .id = first_case_label.id + case_i }; 4207 4208 for (items) |item| { 4209 const value = (try self.air.value(item, mod)) orelse { 4210 return self.todo("switch on runtime value???", .{}); 4211 }; 4212 const int_val = switch (cond_ty.zigTypeTag(mod)) { 4213 .Bool, .Int => if (cond_ty.isSignedInt(mod)) @as(u64, @bitCast(value.toSignedInt(mod))) else value.toUnsignedInt(mod), 4214 .Enum => blk: { 4215 // TODO: figure out of cond_ty is correct (something with enum literals) 4216 break :blk (try value.intFromEnum(cond_ty, mod)).toUnsignedInt(mod); // TODO: composite integer constants 4217 }, 4218 .ErrorSet => value.getErrorInt(mod), 4219 else => unreachable, 4220 }; 4221 const int_lit: spec.LiteralContextDependentNumber = switch (cond_words) { 4222 1 => .{ .uint32 = @as(u32, @intCast(int_val)) }, 4223 2 => .{ .uint64 = int_val }, 4224 else => unreachable, 4225 }; 4226 self.func.body.writeOperand(spec.LiteralContextDependentNumber, int_lit); 4227 self.func.body.writeOperand(IdRef, label); 4228 } 4229 } 4230 } 4231 4232 // Now, finally, we can start emitting each of the cases. 4233 var extra_index: usize = switch_br.end; 4234 var case_i: u32 = 0; 4235 while (case_i < num_cases) : (case_i += 1) { 4236 const case = self.air.extraData(Air.SwitchBr.Case, extra_index); 4237 const items = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[case.end..][0..case.data.items_len])); 4238 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; 4239 extra_index = case.end + case.data.items_len + case_body.len; 4240 4241 const label = IdResult{ .id = first_case_label.id + case_i }; 4242 4243 try self.beginSpvBlock(label); 4244 try self.genBody(case_body); 4245 } 4246 4247 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; 4248 try self.beginSpvBlock(default); 4249 if (else_body.len != 0) { 4250 try self.genBody(else_body); 4251 } else { 4252 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); 4253 } 4254 } 4255 4256 fn airUnreach(self: *DeclGen) !void { 4257 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); 4258 } 4259 4260 fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void { 4261 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; 4262 const mod = self.module; 4263 const decl = mod.declPtr(self.decl_index); 4264 const path = decl.getFileScope(mod).sub_file_path; 4265 const src_fname_id = try self.spv.resolveSourceFileName(path); 4266 const base_line = self.base_line_stack.getLast(); 4267 try self.func.body.emit(self.spv.gpa, .OpLine, .{ 4268 .file = src_fname_id, 4269 .line = base_line + dbg_stmt.line + 1, 4270 .column = dbg_stmt.column + 1, 4271 }); 4272 } 4273 4274 fn airDbgInlineBegin(self: *DeclGen, inst: Air.Inst.Index) !void { 4275 const mod = self.module; 4276 const fn_ty = self.air.instructions.items(.data)[inst].ty_fn; 4277 const decl_index = mod.funcInfo(fn_ty.func).owner_decl; 4278 const decl = mod.declPtr(decl_index); 4279 try self.base_line_stack.append(self.gpa, decl.src_line); 4280 } 4281 4282 fn airDbgInlineEnd(self: *DeclGen, inst: Air.Inst.Index) !void { 4283 _ = inst; 4284 _ = self.base_line_stack.pop(); 4285 } 4286 4287 fn airDbgVar(self: *DeclGen, inst: Air.Inst.Index) !void { 4288 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 4289 const target_id = try self.resolve(pl_op.operand); 4290 const name = self.air.nullTerminatedString(pl_op.payload); 4291 try self.spv.debugName(target_id, name); 4292 } 4293 4294 fn airAssembly(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { 4295 const mod = self.module; 4296 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 4297 const extra = self.air.extraData(Air.Asm, ty_pl.payload); 4298 4299 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; 4300 const clobbers_len = @as(u31, @truncate(extra.data.flags)); 4301 4302 if (!is_volatile and self.liveness.isUnused(inst)) return null; 4303 4304 var extra_i: usize = extra.end; 4305 const outputs = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[extra_i..][0..extra.data.outputs_len])); 4306 extra_i += outputs.len; 4307 const inputs = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[extra_i..][0..extra.data.inputs_len])); 4308 extra_i += inputs.len; 4309 4310 if (outputs.len > 1) { 4311 return self.todo("implement inline asm with more than 1 output", .{}); 4312 } 4313 4314 var output_extra_i = extra_i; 4315 for (outputs) |output| { 4316 if (output != .none) { 4317 return self.todo("implement inline asm with non-returned output", .{}); 4318 } 4319 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 4320 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 4321 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 4322 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 4323 // TODO: Record output and use it somewhere. 4324 } 4325 4326 var input_extra_i = extra_i; 4327 for (inputs) |input| { 4328 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 4329 const constraint = std.mem.sliceTo(extra_bytes, 0); 4330 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 4331 // This equation accounts for the fact that even if we have exactly 4 bytes 4332 // for the string, we still use the next u32 for the null terminator. 4333 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 4334 // TODO: Record input and use it somewhere. 4335 _ = input; 4336 } 4337 4338 { 4339 var clobber_i: u32 = 0; 4340 while (clobber_i < clobbers_len) : (clobber_i += 1) { 4341 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 4342 extra_i += clobber.len / 4 + 1; 4343 // TODO: Record clobber and use it somewhere. 4344 } 4345 } 4346 4347 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; 4348 4349 var as = SpvAssembler{ 4350 .gpa = self.gpa, 4351 .src = asm_source, 4352 .spv = self.spv, 4353 .func = &self.func, 4354 }; 4355 defer as.deinit(); 4356 4357 for (inputs) |input| { 4358 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[input_extra_i..]); 4359 const constraint = std.mem.sliceTo(extra_bytes, 0); 4360 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 4361 // This equation accounts for the fact that even if we have exactly 4 bytes 4362 // for the string, we still use the next u32 for the null terminator. 4363 input_extra_i += (constraint.len + name.len + (2 + 3)) / 4; 4364 4365 const value = try self.resolve(input); 4366 try as.value_map.put(as.gpa, name, .{ .value = value }); 4367 } 4368 4369 as.assemble() catch |err| switch (err) { 4370 error.AssembleFail => { 4371 // TODO: For now the compiler only supports a single error message per decl, 4372 // so to translate the possible multiple errors from the assembler, emit 4373 // them as notes here. 4374 // TODO: Translate proper error locations. 4375 assert(as.errors.items.len != 0); 4376 assert(self.error_msg == null); 4377 const loc = LazySrcLoc.nodeOffset(0); 4378 const src_loc = loc.toSrcLoc(self.module.declPtr(self.decl_index), mod); 4379 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{}); 4380 const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len); 4381 4382 // Sub-scope to prevent `return error.CodegenFail` from running the errdefers. 4383 { 4384 errdefer self.module.gpa.free(notes); 4385 var i: usize = 0; 4386 errdefer for (notes[0..i]) |*note| { 4387 note.deinit(self.module.gpa); 4388 }; 4389 4390 while (i < as.errors.items.len) : (i += 1) { 4391 notes[i] = try Module.ErrorMsg.init(self.module.gpa, src_loc, "{s}", .{as.errors.items[i].msg}); 4392 } 4393 } 4394 self.error_msg.?.notes = notes; 4395 return error.CodegenFail; 4396 }, 4397 else => |others| return others, 4398 }; 4399 4400 for (outputs) |output| { 4401 _ = output; 4402 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[output_extra_i..]); 4403 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[output_extra_i..]), 0); 4404 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 4405 output_extra_i += (constraint.len + name.len + (2 + 3)) / 4; 4406 4407 const result = as.value_map.get(name) orelse return { 4408 return self.fail("invalid asm output '{s}'", .{name}); 4409 }; 4410 4411 switch (result) { 4412 .just_declared, .unresolved_forward_reference => unreachable, 4413 .ty => return self.fail("cannot return spir-v type as value from assembly", .{}), 4414 .value => |ref| return ref, 4415 } 4416 4417 // TODO: Multiple results 4418 } 4419 4420 return null; 4421 } 4422 4423 fn airCall(self: *DeclGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !?IdRef { 4424 _ = modifier; 4425 4426 const mod = self.module; 4427 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 4428 const extra = self.air.extraData(Air.Call, pl_op.payload); 4429 const args = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len])); 4430 const callee_ty = self.typeOf(pl_op.operand); 4431 const zig_fn_ty = switch (callee_ty.zigTypeTag(mod)) { 4432 .Fn => callee_ty, 4433 .Pointer => return self.fail("cannot call function pointers", .{}), 4434 else => unreachable, 4435 }; 4436 const fn_info = mod.typeToFunc(zig_fn_ty).?; 4437 const return_type = fn_info.return_type; 4438 4439 const result_type_ref = try self.resolveFnReturnType(return_type.toType()); 4440 const result_id = self.spv.allocId(); 4441 const callee_id = try self.resolve(pl_op.operand); 4442 4443 const params = try self.gpa.alloc(spec.IdRef, args.len); 4444 defer self.gpa.free(params); 4445 4446 var n_params: usize = 0; 4447 for (args) |arg| { 4448 // Note: resolve() might emit instructions, so we need to call it 4449 // before starting to emit OpFunctionCall instructions. Hence the 4450 // temporary params buffer. 4451 const arg_ty = self.typeOf(arg); 4452 if (!arg_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; 4453 const arg_id = try self.resolve(arg); 4454 4455 params[n_params] = arg_id; 4456 n_params += 1; 4457 } 4458 4459 try self.func.body.emit(self.spv.gpa, .OpFunctionCall, .{ 4460 .id_result_type = self.typeId(result_type_ref), 4461 .id_result = result_id, 4462 .function = callee_id, 4463 .id_ref_3 = params[0..n_params], 4464 }); 4465 4466 if (return_type == .noreturn_type) { 4467 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); 4468 } 4469 4470 if (self.liveness.isUnused(inst) or !return_type.toType().hasRuntimeBitsIgnoreComptime(mod)) { 4471 return null; 4472 } 4473 4474 return result_id; 4475 } 4476 4477 fn typeOf(self: *DeclGen, inst: Air.Inst.Ref) Type { 4478 const mod = self.module; 4479 return self.air.typeOf(inst, &mod.intern_pool); 4480 } 4481 4482 fn typeOfIndex(self: *DeclGen, inst: Air.Inst.Index) Type { 4483 const mod = self.module; 4484 return self.air.typeOfIndex(inst, &mod.intern_pool); 4485 } 4486 };