| ... | @@ -56,6 +56,25 @@ const Tag = enum { | ... | @@ -56,6 +56,25 @@ const Tag = enum { |
| 56 | type_array, | 56 | type_array, |
| 57 | | 57 | |
| 58 | // -- Values | 58 | // -- Values |
| | 59 | /// Value of type u8 |
| | 60 | /// data is value |
| | 61 | uint8, |
| | 62 | /// Value of type u32 |
| | 63 | /// data is value |
| | 64 | uint32, |
| | 65 | // TODO: More specialized tags here. |
| | 66 | /// Integer value for signed values that are smaller than 32 bits. |
| | 67 | /// data is pointer to Int32 |
| | 68 | int_small, |
| | 69 | /// Integer value for unsigned values that are smaller than 32 bits. |
| | 70 | /// data is pointer to UInt32 |
| | 71 | uint_small, |
| | 72 | /// Integer value for signed values that are beteen 32 and 64 bits. |
| | 73 | /// data is pointer to Int64 |
| | 74 | int_large, |
| | 75 | /// Integer value for unsinged values that are beteen 32 and 64 bits. |
| | 76 | /// data is pointer to UInt64 |
| | 77 | uint_large, |
| 59 | /// Value of type f16 | 78 | /// Value of type f16 |
| 60 | /// data is value | 79 | /// data is value |
| 61 | float16, | 80 | float16, |
| ... | @@ -90,6 +109,52 @@ const Tag = enum { | ... | @@ -90,6 +109,52 @@ const Tag = enum { |
| 90 | return @bitCast(f64, bits); | 109 | return @bitCast(f64, bits); |
| 91 | } | 110 | } |
| 92 | }; | 111 | }; |
| | 112 | |
| | 113 | const Int32 = struct { |
| | 114 | ty: Ref, |
| | 115 | value: i32, |
| | 116 | }; |
| | 117 | |
| | 118 | const UInt32 = struct { |
| | 119 | ty: Ref, |
| | 120 | value: u32, |
| | 121 | }; |
| | 122 | |
| | 123 | const UInt64 = struct { |
| | 124 | ty: Ref, |
| | 125 | low: u32, |
| | 126 | high: u32, |
| | 127 | |
| | 128 | fn encode(ty: Ref, value: u64) Int64 { |
| | 129 | return .{ |
| | 130 | .ty = ty, |
| | 131 | .low = @truncate(u32, value), |
| | 132 | .high = @truncate(u32, value >> 32), |
| | 133 | }; |
| | 134 | } |
| | 135 | |
| | 136 | fn decode(self: UInt64) u64 { |
| | 137 | return @as(u64, self.low) | (@as(u64, self.high) << 32); |
| | 138 | } |
| | 139 | }; |
| | 140 | |
| | 141 | const Int64 = struct { |
| | 142 | ty: Ref, |
| | 143 | low: u32, |
| | 144 | high: u32, |
| | 145 | |
| | 146 | fn encode(ty: Ref, value: i64) Int64 { |
| | 147 | return .{ |
| | 148 | .ty = ty, |
| | 149 | .low = @truncate(u32, @bitCast(u64, value)), |
| | 150 | .high = @truncate(u32, @bitCast(u64, value) >> 32), |
| | 151 | }; |
| | 152 | } |
| | 153 | |
| | 154 | fn decode(self: Int64) i64 { |
| | 155 | return @bitCast(i64, @as(u64, self.low) | (@as(u64, self.high) << 32)); |
| | 156 | } |
| | 157 | }; |
| 93 | }; | 158 | }; |
| 94 | | 159 | |
| 95 | pub const Ref = enum(u32) { _ }; | 160 | pub const Ref = enum(u32) { _ }; |
| ... | @@ -108,6 +173,7 @@ pub const Key = union(enum) { | ... | @@ -108,6 +173,7 @@ pub const Key = union(enum) { |
| 108 | array_type: ArrayType, | 173 | array_type: ArrayType, |
| 109 | | 174 | |
| 110 | // -- values | 175 | // -- values |
| | 176 | int: Int, |
| 111 | float: Float, | 177 | float: Float, |
| 112 | | 178 | |
| 113 | pub const IntType = std.builtin.Type.Int; | 179 | pub const IntType = std.builtin.Type.Int; |
| ... | @@ -128,6 +194,41 @@ pub const Key = union(enum) { | ... | @@ -128,6 +194,41 @@ pub const Key = union(enum) { |
| 128 | stride: u32 = 0, | 194 | stride: u32 = 0, |
| 129 | }; | 195 | }; |
| 130 | | 196 | |
| | 197 | pub const Int = struct { |
| | 198 | /// The type: any bitness integer. |
| | 199 | ty: Ref, |
| | 200 | /// The actual value. Only uint64 and int64 types |
| | 201 | /// are available here: Smaller types should use these |
| | 202 | /// fields. |
| | 203 | value: Value, |
| | 204 | |
| | 205 | pub const Value = union(enum) { |
| | 206 | uint64: u64, |
| | 207 | int64: i64, |
| | 208 | }; |
| | 209 | |
| | 210 | /// Turns this value into the corresponding 32-bit literal, 2s complement signed. |
| | 211 | fn toBits32(self: Int) u32 { |
| | 212 | return switch (self.value) { |
| | 213 | .uint64 => |val| @intCast(u32, val), |
| | 214 | .int64 => |val| if (val < 0) @bitCast(u32, @intCast(i32, val)) else @intCast(u32, val), |
| | 215 | }; |
| | 216 | } |
| | 217 | |
| | 218 | fn toBits64(self: Int) u64 { |
| | 219 | return switch (self.value) { |
| | 220 | .uint64 => |val| val, |
| | 221 | .int64 => |val| @bitCast(u64, val), |
| | 222 | }; |
| | 223 | } |
| | 224 | |
| | 225 | fn to(self: Int, comptime T: type) T { |
| | 226 | return switch (self.value) { |
| | 227 | inline else => |val| @intCast(T, val), |
| | 228 | }; |
| | 229 | } |
| | 230 | }; |
| | 231 | |
| 131 | /// Represents a numberic value of some type. | 232 | /// Represents a numberic value of some type. |
| 132 | pub const Float = struct { | 233 | pub const Float = struct { |
| 133 | /// The type: 16, 32, or 64-bit float. | 234 | /// The type: 16, 32, or 64-bit float. |
| ... | @@ -212,6 +313,7 @@ fn emit( | ... | @@ -212,6 +313,7 @@ fn emit( |
| 212 | section: *Section, | 313 | section: *Section, |
| 213 | ) !void { | 314 | ) !void { |
| 214 | const key = self.lookup(ref); | 315 | const key = self.lookup(ref); |
| | 316 | const Lit = spec.LiteralContextDependentNumber; |
| 215 | switch (key) { | 317 | switch (key) { |
| 216 | .void_type => { | 318 | .void_type => { |
| 217 | try section.emit(spv.gpa, .OpTypeVoid, .{ .id_result = result_id }); | 319 | try section.emit(spv.gpa, .OpTypeVoid, .{ .id_result = result_id }); |
| ... | @@ -260,9 +362,24 @@ fn emit( | ... | @@ -260,9 +362,24 @@ fn emit( |
| 260 | try spv.decorate(result_id, .{ .ArrayStride = .{ .array_stride = array.stride } }); | 362 | try spv.decorate(result_id, .{ .ArrayStride = .{ .array_stride = array.stride } }); |
| 261 | } | 363 | } |
| 262 | }, | 364 | }, |
| | 365 | .int => |int| { |
| | 366 | const int_type = self.lookup(int.ty).int_type; |
| | 367 | const ty_id = self.resultId(int.ty); |
| | 368 | const lit: Lit = switch (int_type.bits) { |
| | 369 | 1...32 => .{ .uint32 = int.toBits32() }, |
| | 370 | 33...64 => .{ .uint64 = int.toBits64() }, |
| | 371 | else => unreachable, |
| | 372 | }; |
| | 373 | |
| | 374 | try section.emit(spv.gpa, .OpConstant, .{ |
| | 375 | .id_result_type = ty_id, |
| | 376 | .id_result = result_id, |
| | 377 | .value = lit, |
| | 378 | }); |
| | 379 | }, |
| 263 | .float => |float| { | 380 | .float => |float| { |
| 264 | const ty_id = self.resultId(float.ty); | 381 | const ty_id = self.resultId(float.ty); |
| 265 | const lit: spec.LiteralContextDependentNumber = switch (float.value) { | 382 | const lit: Lit = switch (float.value) { |
| 266 | .float16 => |value| .{ .uint32 = @bitCast(u16, value) }, | 383 | .float16 => |value| .{ .uint32 = @bitCast(u16, value) }, |
| 267 | .float32 => |value| .{ .float32 = value }, | 384 | .float32 => |value| .{ .float32 = value }, |
| 268 | .float64 => |value| .{ .float64 = value }, | 385 | .float64 => |value| .{ .float64 = value }, |
| ... | @@ -330,6 +447,58 @@ pub fn resolve(self: *Self, spv: *Module, key: Key) !Ref { | ... | @@ -330,6 +447,58 @@ pub fn resolve(self: *Self, spv: *Module, key: Key) !Ref { |
| 330 | .result_id = result_id, | 447 | .result_id = result_id, |
| 331 | .data = try self.addExtra(spv, array), | 448 | .data = try self.addExtra(spv, array), |
| 332 | }, | 449 | }, |
| | 450 | .int => |int| blk: { |
| | 451 | const int_type = self.lookup(int.ty).int_type; |
| | 452 | if (int_type.signedness == .unsigned and int_type.bits == 8) { |
| | 453 | break :blk .{ |
| | 454 | .tag = .uint8, |
| | 455 | .result_id = result_id, |
| | 456 | .data = int.to(u8), |
| | 457 | }; |
| | 458 | } else if (int_type.signedness == .unsigned and int_type.bits == 32) { |
| | 459 | break :blk .{ |
| | 460 | .tag = .uint32, |
| | 461 | .result_id = result_id, |
| | 462 | .data = int.to(u32), |
| | 463 | }; |
| | 464 | } |
| | 465 | |
| | 466 | switch (int.value) { |
| | 467 | inline else => |val| { |
| | 468 | if (val >= 0 and val <= std.math.maxInt(u32)) { |
| | 469 | break :blk .{ |
| | 470 | .tag = .uint_small, |
| | 471 | .result_id = result_id, |
| | 472 | .data = try self.addExtra(spv, Tag.UInt32{ |
| | 473 | .ty = int.ty, |
| | 474 | .value = @intCast(u32, val), |
| | 475 | }), |
| | 476 | }; |
| | 477 | } else if (val >= std.math.minInt(i32) and val <= std.math.maxInt(i32)) { |
| | 478 | break :blk .{ |
| | 479 | .tag = .int_small, |
| | 480 | .result_id = result_id, |
| | 481 | .data = try self.addExtra(spv, Tag.Int32{ |
| | 482 | .ty = int.ty, |
| | 483 | .value = @intCast(i32, val), |
| | 484 | }), |
| | 485 | }; |
| | 486 | } else if (val < 0) { |
| | 487 | break :blk .{ |
| | 488 | .tag = .int_large, |
| | 489 | .result_id = result_id, |
| | 490 | .data = try self.addExtra(spv, Tag.Int64.encode(int.ty, @intCast(i64, val))), |
| | 491 | }; |
| | 492 | } else { |
| | 493 | break :blk .{ |
| | 494 | .tag = .uint_large, |
| | 495 | .result_id = result_id, |
| | 496 | .data = try self.addExtra(spv, Tag.UInt64.encode(int.ty, @intCast(u64, val))), |
| | 497 | }; |
| | 498 | } |
| | 499 | }, |
| | 500 | } |
| | 501 | }, |
| 333 | .float => |float| switch (self.lookup(float.ty).float_type.bits) { | 502 | .float => |float| switch (self.lookup(float.ty).float_type.bits) { |
| 334 | 16 => .{ | 503 | 16 => .{ |
| 335 | .tag = .float16, | 504 | .tag = .float16, |
| ... | @@ -391,9 +560,45 @@ pub fn lookup(self: *const Self, ref: Ref) Key { | ... | @@ -391,9 +560,45 @@ pub fn lookup(self: *const Self, ref: Ref) Key { |
| 391 | .value = .{ .float32 = @bitCast(f32, data) }, | 560 | .value = .{ .float32 = @bitCast(f32, data) }, |
| 392 | } }, | 561 | } }, |
| 393 | .float64 => .{ .float = .{ | 562 | .float64 => .{ .float = .{ |
| 394 | .ty = self.get(.{ .float_type = .{ .bits = 32 } }), | 563 | .ty = self.get(.{ .float_type = .{ .bits = 64 } }), |
| 395 | .value = .{ .float64 = self.extraData(Tag.Float64, data).decode() }, | 564 | .value = .{ .float64 = self.extraData(Tag.Float64, data).decode() }, |
| 396 | } }, | 565 | } }, |
| | 566 | .uint8 => .{ .int = .{ |
| | 567 | .ty = self.get(.{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }), |
| | 568 | .value = .{ .uint64 = data }, |
| | 569 | } }, |
| | 570 | .uint32 => .{ .int = .{ |
| | 571 | .ty = self.get(.{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }), |
| | 572 | .value = .{ .uint64 = data }, |
| | 573 | } }, |
| | 574 | .int_small => { |
| | 575 | const payload = self.extraData(Tag.Int32, data); |
| | 576 | return .{ .int = .{ |
| | 577 | .ty = payload.ty, |
| | 578 | .value = .{ .int64 = payload.value }, |
| | 579 | } }; |
| | 580 | }, |
| | 581 | .uint_small => { |
| | 582 | const payload = self.extraData(Tag.UInt32, data); |
| | 583 | return .{ .int = .{ |
| | 584 | .ty = payload.ty, |
| | 585 | .value = .{ .uint64 = payload.value }, |
| | 586 | } }; |
| | 587 | }, |
| | 588 | .int_large => { |
| | 589 | const payload = self.extraData(Tag.Int64, data); |
| | 590 | return .{ .int = .{ |
| | 591 | .ty = payload.ty, |
| | 592 | .value = .{ .int64 = payload.decode() }, |
| | 593 | } }; |
| | 594 | }, |
| | 595 | .uint_large => { |
| | 596 | const payload = self.extraData(Tag.UInt64, data); |
| | 597 | return .{ .int = .{ |
| | 598 | .ty = payload.ty, |
| | 599 | .value = .{ .uint64 = payload.decode() }, |
| | 600 | } }; |
| | 601 | }, |
| 397 | }; | 602 | }; |
| 398 | } | 603 | } |
| 399 | | 604 | |
| ... | @@ -409,6 +614,7 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) !u32 { | ... | @@ -409,6 +614,7 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) !u32 { |
| 409 | const field_val = @field(extra, field.name); | 614 | const field_val = @field(extra, field.name); |
| 410 | const word = switch (field.type) { | 615 | const word = switch (field.type) { |
| 411 | u32 => field_val, | 616 | u32 => field_val, |
| | 617 | i32 => @bitCast(u32, field_val), |
| 412 | Ref => @enumToInt(field_val), | 618 | Ref => @enumToInt(field_val), |
| 413 | else => @compileError("Invalid type: " ++ @typeName(field.type)), | 619 | else => @compileError("Invalid type: " ++ @typeName(field.type)), |
| 414 | }; | 620 | }; |
| ... | @@ -428,6 +634,7 @@ fn extraDataTrail(self: Self, comptime T: type, offset: u32) struct { data: T, t | ... | @@ -428,6 +634,7 @@ fn extraDataTrail(self: Self, comptime T: type, offset: u32) struct { data: T, t |
| 428 | const word = self.extra.items[offset + i]; | 634 | const word = self.extra.items[offset + i]; |
| 429 | @field(result, field.name) = switch (field.type) { | 635 | @field(result, field.name) = switch (field.type) { |
| 430 | u32 => word, | 636 | u32 => word, |
| | 637 | i32 => @bitCast(i32, word), |
| 431 | Ref => @intToEnum(Ref, word), | 638 | Ref => @intToEnum(Ref, word), |
| 432 | else => @compileError("Invalid type: " ++ @typeName(field.type)), | 639 | else => @compileError("Invalid type: " ++ @typeName(field.type)), |
| 433 | }; | 640 | }; |