diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen/spirv.zig | 95 | ||||
| -rw-r--r-- | src/codegen/spirv/spec.zig | 120 | ||||
| -rw-r--r-- | src/link/SpirV.zig | 119 |
3 files changed, 221 insertions, 113 deletions
diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 23fc45616f..077e71d4e1 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1,9 +1,13 @@ const std = @import("std"); const Allocator = std.mem.Allocator; +const log = std.log.scoped(.codegen); const spec = @import("spirv/spec.zig"); const Module = @import("../Module.zig"); const Decl = Module.Decl; +const Type = @import("../type.zig").Type; + +pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void { const word_count = @intCast(u32, args.len + 1); @@ -12,38 +16,91 @@ pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []c } pub const SPIRVModule = struct { - next_id: u32 = 0, - free_id_list: std.ArrayList(u32), + next_result_id: u32 = 0, + + target: std.Target, + + types: TypeMap, + + types_and_globals: std.ArrayList(u32), + fn_decls: std.ArrayList(u32), - pub fn init(allocator: *Allocator) SPIRVModule { + pub fn init(target: std.Target, allocator: *Allocator) SPIRVModule { return .{ - .free_id_list = std.ArrayList(u32).init(allocator), + .target = target, + .types = TypeMap.init(allocator), + .types_and_globals = std.ArrayList(u32).init(allocator), + .fn_decls = std.ArrayList(u32).init(allocator), }; } pub fn deinit(self: *SPIRVModule) void { - self.free_id_list.deinit(); + self.fn_decls.deinit(); + self.types_and_globals.deinit(); + self.types.deinit(); + self.* = undefined; } - pub fn allocId(self: *SPIRVModule) u32 { - if (self.free_id_list.popOrNull()) |id| return id; + pub fn allocResultId(self: *SPIRVModule) u32 { + defer self.next_result_id += 1; + return self.next_result_id; + } - defer self.next_id += 1; - return self.next_id; + pub fn resultIdBound(self: *SPIRVModule) u32 { + return self.next_result_id; } - pub fn freeId(self: *SPIRVModule, id: u32) void { - if (id + 1 == self.next_id) { - self.next_id -= 1; - } else { - // If no more memory to append the id to the free list, just ignore it. - self.free_id_list.append(id) catch {}; + pub fn getOrGenType(self: *SPIRVModule, t: Type) !u32 { + // We can't use getOrPut here so we can recursively generate types. + if (self.types.get(t)) |already_generated| { + return already_generated; } - } - pub fn idBound(self: *SPIRVModule) u32 { - return self.next_id; + const result = self.allocResultId(); + + switch (t.zigTypeTag()) { + .Void => try writeInstruction(&self.types_and_globals, .OpTypeVoid, &[_]u32{ result }), + .Bool => try writeInstruction(&self.types_and_globals, .OpTypeBool, &[_]u32{ result }), + .Int => { + const int_info = t.intInfo(self.target); + try writeInstruction(&self.types_and_globals, .OpTypeInt, &[_]u32{ + result, + int_info.bits, + switch (int_info.signedness) { + .unsigned => 0, + .signed => 1, + }, + }); + }, + // TODO: Verify that floatBits() will be correct. + .Float => try writeInstruction(&self.types_and_globals, .OpTypeFloat, &[_]u32{ result, t.floatBits(self.target) }), + .Null, + .Undefined, + .EnumLiteral, + .ComptimeFloat, + .ComptimeInt, + .Type, + => unreachable, // Must be const or comptime. + + .BoundFn => unreachable, // this type will be deleted from the language. + + else => return error.TODO, + } + + try self.types.put(t, result); + return result; } - pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void {} + pub fn gen(self: *SPIRVModule, decl: *Decl) !void { + const typed_value = decl.typed_value.most_recent.typed_value; + + switch (typed_value.ty.zigTypeTag()) { + .Fn => { + log.debug("Generating code for function '{s}'", .{ std.mem.spanZ(decl.name) }); + + _ = try self.getOrGenType(typed_value.ty.fnReturnType()); + }, + else => return error.TODO, + } + } }; diff --git a/src/codegen/spirv/spec.zig b/src/codegen/spirv/spec.zig index ceb62f1e5d..7dc1e95ad1 100644 --- a/src/codegen/spirv/spec.zig +++ b/src/codegen/spirv/spec.zig @@ -1,26 +1,5 @@ -// Copyright (c) 2014-2020 The Khronos Group Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and/or associated documentation files (the "Materials"), -// to deal in the Materials without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Materials, and to permit persons to whom the -// Materials are furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Materials. -// -// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS -// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND -// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ -// -// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS -// IN THE MATERIALS. +//! This file is auto-generated by tools/gen_spirv_spec.zig. + const Version = @import("builtin").Version; pub const version = Version{ .major = 1, .minor = 5, .patch = 4 }; pub const magic_number: u32 = 0x07230203; @@ -443,8 +422,15 @@ pub const Opcode = extern enum(u16) { OpUSubSatINTEL = 5596, OpIMul32x16INTEL = 5597, OpUMul32x16INTEL = 5598, - OpFunctionPointerINTEL = 5600, + OpConstFunctionPointerINTEL = 5600, OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, OpDecorateString = 5632, OpDecorateStringGOOGLE = 5632, OpMemberDecorateString = 5633, @@ -567,7 +553,12 @@ pub const Opcode = extern enum(u16) { OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, OpLoopControlINTEL = 5887, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrINTEL = 5938, OpReadPipeBlockingINTEL = 5946, OpWritePipeBlockingINTEL = 5947, OpFPGARegINTEL = 5949, @@ -589,6 +580,10 @@ pub const Opcode = extern enum(u16) { OpRayQueryGetIntersectionObjectToWorldKHR = 6031, OpRayQueryGetIntersectionWorldToObjectKHR = 6032, OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, _, }; pub const ImageOperands = packed struct { @@ -642,8 +637,8 @@ pub const FPFastMathMode = packed struct { _reserved_bit_13: bool = false, _reserved_bit_14: bool = false, _reserved_bit_15: bool = false, - _reserved_bit_16: bool = false, - _reserved_bit_17: bool = false, + AllowContractFastINTEL: bool = false, + AllowReassocINTEL: bool = false, _reserved_bit_18: bool = false, _reserved_bit_19: bool = false, _reserved_bit_20: bool = false, @@ -717,7 +712,7 @@ pub const LoopControl = packed struct { LoopCoalesceINTEL: bool = false, MaxInterleavingINTEL: bool = false, SpeculatedIterationsINTEL: bool = false, - _reserved_bit_23: bool = false, + NoFusionINTEL: bool = false, _reserved_bit_24: bool = false, _reserved_bit_25: bool = false, _reserved_bit_26: bool = false, @@ -1037,10 +1032,16 @@ pub const ExecutionMode = extern enum(u32) { SampleInterlockUnorderedEXT = 5369, ShadingRateInterlockOrderedEXT = 5370, ShadingRateInterlockUnorderedEXT = 5371, + SharedLocalMemorySizeINTEL = 5618, + RoundingModeRTPINTEL = 5620, + RoundingModeRTNINTEL = 5621, + FloatingPointModeALTINTEL = 5622, + FloatingPointModeIEEEINTEL = 5623, MaxWorkgroupSizeINTEL = 5893, MaxWorkDimINTEL = 5894, NoGlobalOffsetINTEL = 5895, NumSIMDWorkitemsINTEL = 5896, + SchedulerTargetFmaxMhzINTEL = 5903, _, }; pub const StorageClass = extern enum(u32) { @@ -1072,6 +1073,8 @@ pub const StorageClass = extern enum(u32) { PhysicalStorageBuffer = 5349, PhysicalStorageBufferEXT = 5349, CodeSectionINTEL = 5605, + DeviceOnlyINTEL = 5936, + HostOnlyINTEL = 5937, _, }; pub const Dim = extern enum(u32) { @@ -1192,9 +1195,20 @@ pub const FPRoundingMode = extern enum(u32) { RTN = 3, _, }; +pub const FPDenormMode = extern enum(u32) { + Preserve = 0, + FlushToZero = 1, + _, +}; +pub const FPOperationMode = extern enum(u32) { + IEEE = 0, + ALT = 1, + _, +}; pub const LinkageType = extern enum(u32) { Export = 0, Import = 1, + LinkOnceODR = 2, _, }; pub const AccessQualifier = extern enum(u32) { @@ -1279,12 +1293,22 @@ pub const Decoration = extern enum(u32) { RestrictPointerEXT = 5355, AliasedPointer = 5356, AliasedPointerEXT = 5356, + SIMTCallINTEL = 5599, ReferencedIndirectlyINTEL = 5602, + ClobberINTEL = 5607, + SideEffectsINTEL = 5608, + VectorComputeVariableINTEL = 5624, + FuncParamIOKindINTEL = 5625, + VectorComputeFunctionINTEL = 5626, + StackCallINTEL = 5627, + GlobalVariableOffsetINTEL = 5628, CounterBuffer = 5634, HlslCounterBufferGOOGLE = 5634, UserSemantic = 5635, HlslSemanticGOOGLE = 5635, UserTypeGOOGLE = 5636, + FunctionRoundingModeINTEL = 5822, + FunctionDenormModeINTEL = 5823, RegisterINTEL = 5825, MemoryINTEL = 5826, NumbanksINTEL = 5827, @@ -1297,6 +1321,17 @@ pub const Decoration = extern enum(u32) { MergeINTEL = 5834, BankBitsINTEL = 5835, ForcePow2DepthINTEL = 5836, + BurstCoalesceINTEL = 5899, + CacheSizeINTEL = 5900, + DontStaticallyCoalesceINTEL = 5901, + PrefetchINTEL = 5902, + StallEnableINTEL = 5905, + FuseLoopsInFunctionINTEL = 5907, + BufferLocationINTEL = 5921, + IOPipeStorageINTEL = 5944, + FunctionFloatingPointModeINTEL = 6080, + SingleElementVectorINTEL = 6085, + VectorComputeCallableFunctionINTEL = 6087, _, }; pub const BuiltIn = extern enum(u32) { @@ -1342,14 +1377,14 @@ pub const BuiltIn = extern enum(u32) { VertexIndex = 42, InstanceIndex = 43, SubgroupEqMask = 4416, - SubgroupGeMask = 4417, - SubgroupGtMask = 4418, - SubgroupLeMask = 4419, - SubgroupLtMask = 4420, SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, SubgroupLtMaskKHR = 4420, BaseVertex = 4424, BaseInstance = 4425, @@ -1520,6 +1555,9 @@ pub const Capability = extern enum(u32) { FragmentShadingRateKHR = 4422, SubgroupBallotKHR = 4423, DrawParameters = 4427, + WorkgroupMemoryExplicitLayoutKHR = 4428, + WorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, SubgroupVoteKHR = 4431, StorageBuffer16BitAccess = 4433, StorageUniformBufferBlock16 = 4433, @@ -1610,21 +1648,41 @@ pub const Capability = extern enum(u32) { SubgroupBufferBlockIOINTEL = 5569, SubgroupImageBlockIOINTEL = 5570, SubgroupImageMediaBlockIOINTEL = 5579, + RoundToInfinityINTEL = 5582, + FloatingPointModeINTEL = 5583, IntegerFunctions2INTEL = 5584, FunctionPointersINTEL = 5603, IndirectReferencesINTEL = 5604, + AsmINTEL = 5606, + AtomicFloat32MinMaxEXT = 5612, + AtomicFloat64MinMaxEXT = 5613, + AtomicFloat16MinMaxEXT = 5616, + VectorComputeINTEL = 5617, + VectorAnyINTEL = 5619, + ExpectAssumeKHR = 5629, SubgroupAvcMotionEstimationINTEL = 5696, SubgroupAvcMotionEstimationIntraINTEL = 5697, SubgroupAvcMotionEstimationChromaINTEL = 5698, + VariableLengthArrayINTEL = 5817, + FunctionFloatControlINTEL = 5821, FPGAMemoryAttributesINTEL = 5824, + FPFastMathModeINTEL = 5837, + ArbitraryPrecisionIntegersINTEL = 5844, UnstructuredLoopControlsINTEL = 5886, FPGALoopControlsINTEL = 5888, KernelAttributesINTEL = 5892, FPGAKernelAttributesINTEL = 5897, + FPGAMemoryAccessesINTEL = 5898, + FPGAClusterAttributesINTEL = 5904, + LoopFuseINTEL = 5906, + FPGABufferLocationINTEL = 5920, + USMStorageClassesINTEL = 5935, + IOPipesINTEL = 5943, BlockingPipesINTEL = 5945, FPGARegINTEL = 5948, AtomicFloat32AddEXT = 6033, AtomicFloat64AddEXT = 6034, + LongConstantCompositeINTEL = 6089, _, }; pub const RayQueryIntersection = extern enum(u32) { diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 7a35752f62..95c747c170 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -16,11 +16,16 @@ //! All function declarations without a body (extern functions presumably). //! All regular functions. +// Because SPIR-V requires re-compilation anyway, and so hot swapping will not work +// anyway, we simply generate all the code in flushModule. This keeps +// things considerably simpler. + const SpirV = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const log = std.log.scoped(.link); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); @@ -30,16 +35,15 @@ const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const spec = @import("../codegen/spirv/spec.zig"); +// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl? pub const FnData = struct { - id: ?u32 = null, - code: std.ArrayListUnmanaged(u32) = .{}, + // We're going to fill these in flushModule, and we're going to fill them unconditionally, + // so just set it to undefined. + id: u32 = undefined }; base: link.File, -// TODO: Does this file need to support multiple independent modules? -spirv_module: codegen.SPIRVModule, - pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { const spirv = try gpa.create(SpirV); spirv.* = .{ @@ -49,7 +53,6 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { .file = null, .allocator = gpa, }, - .spirv_module = codegen.SPIRVModule.init(gpa), }; // TODO: Figure out where to put all of these @@ -87,28 +90,9 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio return spirv; } -pub fn deinit(self: *SpirV) void { - self.spirv_module.deinit(); -} - -pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const fn_data = &decl.fn_link.spirv; - if (fn_data.id == null) { - fn_data.id = self.spirv_module.allocId(); - } - - var managed_code = fn_data.code.toManaged(self.base.allocator); - managed_code.items.len = 0; - - try self.spirv_module.genDecl(fn_data.id.?, &managed_code, decl); - fn_data.code = managed_code.toUnmanaged(); +pub fn deinit(self: *SpirV) void {} - // Free excess allocated memory for this Decl. - fn_data.code.shrinkAndFree(self.base.allocator, fn_data.code.items.len); -} +pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {} pub fn updateDeclExports( self: *SpirV, @@ -117,12 +101,7 @@ pub fn updateDeclExports( exports: []const *Module.Export, ) !void {} -pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { - var fn_data = decl.fn_link.spirv; - fn_data.code.deinit(self.base.allocator); - if (fn_data.id) |id| self.spirv_module.freeId(id); - decl.fn_link.spirv = undefined; -} +pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {} pub fn flush(self: *SpirV, comp: *Compilation) !void { if (build_options.have_llvm and self.base.options.use_lld) { @@ -139,55 +118,69 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { const module = self.base.options.module.?; const target = comp.getTarget(); + var spirv_module = codegen.SPIRVModule.init(target, self.base.allocator); + defer spirv_module.deinit(); + + // Allocate an ID for every declaration before generating code, + // so that we can access them before processing them. + // TODO: We're allocating an ID unconditionally now, are there + // declarations which don't generate a result? + // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though. + { + for (module.decl_table.items()) |entry| { + const decl = entry.value; + if (decl.typed_value != .most_recent) + continue; + + decl.fn_link.spirv.id = spirv_module.allocResultId(); + log.debug("Allocating id {} to '{s}'", .{ decl.fn_link.spirv.id, std.mem.spanZ(decl.name) }); + } + } + + // Now, actually generate the code for all declarations. + { + for (module.decl_table.items()) |entry| { + const decl = entry.value; + if (decl.typed_value != .most_recent) + continue; + + try spirv_module.gen(decl); + } + } + var binary = std.ArrayList(u32).init(self.base.allocator); defer binary.deinit(); - // Note: The order of adding sections to the final binary - // follows the SPIR-V logical module format! - try binary.appendSlice(&[_]u32{ spec.magic_number, (spec.version.major << 16) | (spec.version.minor << 8), 0, // TODO: Register Zig compiler magic number. - self.spirv_module.idBound(), + spirv_module.resultIdBound(), // ID bound. 0, // Schema (currently reserved for future use in the SPIR-V spec). }); try writeCapabilities(&binary, target); try writeMemoryModel(&binary, target); - // Collect list of buffers to write. - // SPIR-V files support both little and big endian words. The actual format is - // disambiguated by the magic number, and so theoretically we don't need to worry - // about endian-ness when writing the final binary. - var all_buffers = std.ArrayList(std.os.iovec_const).init(self.base.allocator); - defer all_buffers.deinit(); - - // Pre-allocate enough for the binary info + all functions - try all_buffers.ensureCapacity(module.decl_table.count() + 1); - - all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items)); - - for (module.decl_table.items()) |entry| { - const decl = entry.value; - switch (decl.typed_value) { - .most_recent => |tvm| { - const fn_data = &decl.fn_link.spirv; - all_buffers.appendAssumeCapacity(wordsToIovConst(fn_data.code.items)); - }, - .never_succeeded => continue, - } - } + // Note: The order of adding sections to the final binary + // follows the SPIR-V logical module format! + var all_buffers = [_]std.os.iovec_const{ + wordsToIovConst(binary.items), + wordsToIovConst(spirv_module.types_and_globals.items), + wordsToIovConst(spirv_module.fn_decls.items), + }; + + const file = self.base.file.?; + const bytes = std.mem.sliceAsBytes(binary.items); var file_size: u64 = 0; - for (all_buffers.items) |iov| { + for (all_buffers) |iov| { file_size += iov.iov_len; } - const file = self.base.file.?; try file.seekTo(0); try file.setEndPos(file_size); - try file.pwritevAll(all_buffers.items, 0); + try file.pwritevAll(&all_buffers, 0); } fn writeCapabilities(binary: *std.ArrayList(u32), target: std.Target) !void { @@ -231,4 +224,4 @@ fn wordsToIovConst(words: []const u32) std.os.iovec_const { .iov_base = bytes.ptr, .iov_len = bytes.len, }; -} +}
\ No newline at end of file |
