adding some operators, working on example
This commit is contained in:
@@ -24,7 +24,7 @@ pub fn main() !void {
|
|||||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||||
var output: [4]f32 = undefined;
|
var output: [4]f32 = undefined;
|
||||||
try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color"));
|
try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color"));
|
||||||
std.log.info("Output: Vec4[{d}, {d}, {d}, {d}]", .{ output[0], output[1], output[2], output[3] });
|
std.log.info("Output: Vec4{any}", .{output});
|
||||||
}
|
}
|
||||||
std.log.info("Successfully executed", .{});
|
std.log.info("Successfully executed", .{});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ pub fn build(b: *std.Build) void {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sdl3 = b.lazyDependency("sdl3", .{}) orelse return;
|
||||||
|
example_exe.root_module.addImport("sdl3", sdl3.module("sdl3"));
|
||||||
|
|
||||||
const example_install = b.addInstallArtifact(example_exe, .{});
|
const example_install = b.addInstallArtifact(example_exe, .{});
|
||||||
example_install.step.dependOn(&lib_install.step);
|
example_install.step.dependOn(&lib_install.step);
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
.hash = "NZSL-1.1.2-N0xSVMt6AAC1ncQHA_RafnclWolDA477iTnFmZgdvxd-",
|
.hash = "NZSL-1.1.2-N0xSVMt6AAC1ncQHA_RafnclWolDA477iTnFmZgdvxd-",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
|
.sdl3 = .{
|
||||||
|
.url = "git+https://codeberg.org/7Games/zig-sdl3?ref=master#eefd1b86205ed4fbc5f5274b5ba34aa97798d693",
|
||||||
|
.hash = "sdl3-0.1.6-NmT1Q8kQJgCrbdH9Z3ZmLo5uJ1et3oxhfYazrNTtfsgv",
|
||||||
|
.lazy = true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
.minimum_zig_version = "0.15.2",
|
.minimum_zig_version = "0.15.2",
|
||||||
.paths = .{
|
.paths = .{
|
||||||
|
|||||||
@@ -1,25 +1,95 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const sdl3 = @import("sdl3");
|
||||||
const spv = @import("spv");
|
const spv = @import("spv");
|
||||||
|
|
||||||
const shader_source = @embedFile("shader.spv");
|
const shader_source = @embedFile("shader.spv");
|
||||||
|
|
||||||
|
const screen_width = 640;
|
||||||
|
const screen_height = 480;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
{
|
{
|
||||||
var gpa: std.heap.DebugAllocator(.{}) = .init;
|
var gpa: std.heap.DebugAllocator(.{}) = .init;
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
|
|
||||||
|
defer sdl3.shutdown();
|
||||||
|
const init_flags = sdl3.InitFlags{ .video = true };
|
||||||
|
try sdl3.init(init_flags);
|
||||||
|
defer sdl3.quit(init_flags);
|
||||||
|
|
||||||
|
const window = try sdl3.video.Window.init("Hello SDL3", screen_width, screen_height, .{});
|
||||||
|
defer window.deinit();
|
||||||
|
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source)));
|
var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source)));
|
||||||
defer module.deinit(allocator);
|
defer module.deinit(allocator);
|
||||||
|
|
||||||
var rt = try spv.Runtime.init(allocator, &module);
|
const surface = try window.getSurface();
|
||||||
defer rt.deinit(allocator);
|
try surface.clear(.{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 0.0 });
|
||||||
|
|
||||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
{
|
||||||
var output: [4]i32 = undefined;
|
try surface.lock();
|
||||||
try rt.readOutput(i32, output[0..], try rt.getResultByName("color"));
|
defer surface.unlock();
|
||||||
std.log.info("Output: Vec4{any}", .{output});
|
|
||||||
|
const map = surface.getPixels() orelse return;
|
||||||
|
|
||||||
|
var pool: std.Thread.Pool = undefined;
|
||||||
|
try pool.init(.{
|
||||||
|
.allocator = allocator,
|
||||||
|
.n_jobs = 16384,
|
||||||
|
});
|
||||||
|
defer pool.deinit();
|
||||||
|
|
||||||
|
var wait_group: std.Thread.WaitGroup = .{};
|
||||||
|
|
||||||
|
const margin_x = @divTrunc(screen_width, 5);
|
||||||
|
const margin_y = @divTrunc(screen_height, 5);
|
||||||
|
const top_y = margin_y;
|
||||||
|
const bottom_y = (screen_height - 1) - margin_y;
|
||||||
|
const center_x = @divTrunc(screen_width, 2);
|
||||||
|
const tri_h = bottom_y - top_y;
|
||||||
|
const max_half_w = @divTrunc(screen_width, 2) - margin_x;
|
||||||
|
|
||||||
|
for (top_y..bottom_y) |y| {
|
||||||
|
const t: f32 = @as(f32, @floatFromInt(y - top_y)) / @as(f32, @floatFromInt(tri_h));
|
||||||
|
const half_w: usize = @intFromFloat((t * @as(f32, @floatFromInt(max_half_w))) + 0.5);
|
||||||
|
const x0 = std.math.clamp(center_x - half_w, 0, screen_width - 1);
|
||||||
|
const x1 = std.math.clamp(center_x + half_w, 0, screen_width - 1);
|
||||||
|
|
||||||
|
for (x0..x1) |x| {
|
||||||
|
pool.spawnWg(&wait_group, run, .{ allocator, &module, map, surface, x, y });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pool.waitAndWork(&wait_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
try window.updateSurface();
|
||||||
|
|
||||||
|
std.Thread.sleep(5_000_000_000);
|
||||||
}
|
}
|
||||||
std.log.info("Successfully executed", .{});
|
std.log.info("Successfully executed", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run(allocator: std.mem.Allocator, module: *spv.Module, map: []u8, surface: sdl3.surface.Surface, x: usize, y: usize) void {
|
||||||
|
var rt = spv.Runtime.init(allocator, module) catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)});
|
||||||
|
defer rt.deinit(allocator);
|
||||||
|
|
||||||
|
var output: [4]f32 = undefined;
|
||||||
|
|
||||||
|
const entry = rt.getEntryPointByName("main") catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)});
|
||||||
|
const color = rt.getResultByName("color") catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)});
|
||||||
|
|
||||||
|
rt.callEntryPoint(allocator, entry) catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)});
|
||||||
|
rt.readOutput(f32, output[0..], color) catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)});
|
||||||
|
|
||||||
|
const rgba = surface.mapRgba(
|
||||||
|
@intFromFloat(output[0] * 255.0),
|
||||||
|
@intFromFloat(output[1] * 255.0),
|
||||||
|
@intFromFloat(output[2] * 255.0),
|
||||||
|
@intFromFloat(output[3] * 255.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
var pixel_map: [*]u32 = @as([*]u32, @ptrCast(@alignCast(map.ptr)));
|
||||||
|
pixel_map[(y * surface.getWidth()) + x] = rgba.value;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
[nzsl_version("1.1")]
|
[nzsl_version("1.1")]
|
||||||
module;
|
module;
|
||||||
|
|
||||||
|
struct FragIn
|
||||||
|
{
|
||||||
|
[location(0)] pos: vec2[f32]
|
||||||
|
}
|
||||||
|
|
||||||
struct FragOut
|
struct FragOut
|
||||||
{
|
{
|
||||||
[location(0)] color: vec4[i32]
|
[location(0)] color: vec4[f32]
|
||||||
}
|
}
|
||||||
|
|
||||||
[entry(frag)]
|
[entry(frag)]
|
||||||
fn main() -> FragOut
|
fn main(input: FragIn) -> FragOut
|
||||||
{
|
{
|
||||||
let base: i32 = 4;
|
|
||||||
let value: i32 = base >> 3;
|
|
||||||
let output: FragOut;
|
let output: FragOut;
|
||||||
output.color = vec4[i32](value, value, value, value);
|
output.color = vec4[f32](1.0, 0.0, 0.0, 1.0);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
Version 1.0
|
Version 1.0
|
||||||
Generator: 2560130
|
Generator: 2560130
|
||||||
Bound: 29
|
Bound: 21
|
||||||
Schema: 0
|
Schema: 0
|
||||||
OpCapability Capability(Shader)
|
OpCapability Capability(Shader)
|
||||||
OpMemoryModel AddressingModel(Logical) MemoryModel(GLSL450)
|
OpMemoryModel AddressingModel(Logical) MemoryModel(GLSL450)
|
||||||
@@ -16,35 +16,25 @@ Schema: 0
|
|||||||
OpMemberDecorate %7 0 Decoration(Offset) 0
|
OpMemberDecorate %7 0 Decoration(Offset) 0
|
||||||
%1 = OpTypeVoid
|
%1 = OpTypeVoid
|
||||||
%2 = OpTypeFunction %1
|
%2 = OpTypeFunction %1
|
||||||
%3 = OpTypeInt 32 1
|
%3 = OpTypeFloat 32
|
||||||
%4 = OpTypeVector %3 4
|
%4 = OpTypeVector %3 4
|
||||||
%5 = OpTypePointer StorageClass(Output) %4
|
%5 = OpTypePointer StorageClass(Output) %4
|
||||||
%7 = OpTypeStruct %4
|
%7 = OpTypeStruct %4
|
||||||
%8 = OpConstant %3 i32(4)
|
%8 = OpTypePointer StorageClass(Function) %7
|
||||||
%9 = OpTypePointer StorageClass(Function) %3
|
%9 = OpTypeInt 32 1
|
||||||
%10 = OpConstant %3 i32(3)
|
%10 = OpConstant %9 i32(0)
|
||||||
%11 = OpTypePointer StorageClass(Function) %7
|
%11 = OpConstant %3 f32(1)
|
||||||
%12 = OpConstant %3 i32(0)
|
%12 = OpConstant %3 f32(0)
|
||||||
%26 = OpTypePointer StorageClass(Function) %4
|
%18 = OpTypePointer StorageClass(Function) %4
|
||||||
%6 = OpVariable %5 StorageClass(Output)
|
%6 = OpVariable %5 StorageClass(Output)
|
||||||
%13 = OpFunction %1 FunctionControl(0) %2
|
%13 = OpFunction %1 FunctionControl(0) %2
|
||||||
%14 = OpLabel
|
%14 = OpLabel
|
||||||
%15 = OpVariable %9 StorageClass(Function)
|
%15 = OpVariable %8 StorageClass(Function)
|
||||||
%16 = OpVariable %9 StorageClass(Function)
|
%16 = OpCompositeConstruct %4 %11 %12 %12 %11
|
||||||
%17 = OpVariable %11 StorageClass(Function)
|
%17 = OpAccessChain %18 %15 %10
|
||||||
OpStore %15 %8
|
OpStore %17 %16
|
||||||
%18 = OpLoad %3 %15
|
%19 = OpLoad %7 %15
|
||||||
%19 = OpShiftRightArithmetic %3 %18 %10
|
%20 = OpCompositeExtract %4 %19 0
|
||||||
OpStore %16 %19
|
OpStore %6 %20
|
||||||
%20 = OpLoad %3 %16
|
|
||||||
%21 = OpLoad %3 %16
|
|
||||||
%22 = OpLoad %3 %16
|
|
||||||
%23 = OpLoad %3 %16
|
|
||||||
%24 = OpCompositeConstruct %4 %20 %21 %22 %23
|
|
||||||
%25 = OpAccessChain %26 %17 %12
|
|
||||||
OpStore %25 %24
|
|
||||||
%27 = OpLoad %7 %17
|
|
||||||
%28 = OpCompositeExtract %4 %27 0
|
|
||||||
OpStore %6 %28
|
|
||||||
OpReturn
|
OpReturn
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|||||||
@@ -159,15 +159,15 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S
|
|||||||
capabilities,
|
capabilities,
|
||||||
entry_points,
|
entry_points,
|
||||||
});
|
});
|
||||||
|
|
||||||
//@import("pretty").print(allocator, self.results, .{
|
|
||||||
// .tab_size = 4,
|
|
||||||
// .max_depth = 0,
|
|
||||||
// .struct_max_len = 0,
|
|
||||||
// .array_max_len = 0,
|
|
||||||
//}) catch return ModuleError.OutOfMemory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//@import("pretty").print(allocator, self.results, .{
|
||||||
|
// .tab_size = 4,
|
||||||
|
// .max_depth = 0,
|
||||||
|
// .struct_max_len = 0,
|
||||||
|
// .array_max_len = 0,
|
||||||
|
//}) catch return ModuleError.OutOfMemory;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +205,10 @@ fn populateMaps(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
|
|||||||
for (self.results, 0..) |result, id| {
|
for (self.results, 0..) |result, id| {
|
||||||
if (result.variant == null or std.meta.activeTag(result.variant.?) != .Variable) continue;
|
if (result.variant == null or std.meta.activeTag(result.variant.?) != .Variable) continue;
|
||||||
switch (result.variant.?.Variable.storage_class) {
|
switch (result.variant.?.Variable.storage_class) {
|
||||||
|
.Input => for (result.decorations.items) |decoration| switch (decoration.rtype) {
|
||||||
|
.Location => self.input_locations.append(allocator, @intCast(id)) catch return ModuleError.OutOfMemory,
|
||||||
|
else => {},
|
||||||
|
},
|
||||||
.Output => for (result.decorations.items) |decoration| switch (decoration.rtype) {
|
.Output => for (result.decorations.items) |decoration| switch (decoration.rtype) {
|
||||||
.Location => self.output_locations.append(allocator, @intCast(id)) catch return ModuleError.OutOfMemory,
|
.Location => self.output_locations.append(allocator, @intCast(id)) catch return ModuleError.OutOfMemory,
|
||||||
else => {},
|
else => {},
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ pub const RuntimeError = error{
|
|||||||
InvalidEntryPoint,
|
InvalidEntryPoint,
|
||||||
ToDo,
|
ToDo,
|
||||||
DivisionByZero,
|
DivisionByZero,
|
||||||
|
InvalidValueType,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ReadOutputError = error{
|
||||||
|
NotFound,
|
||||||
|
InvalidValueType,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Function = struct {
|
pub const Function = struct {
|
||||||
@@ -147,11 +153,11 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind
|
|||||||
//}) catch return RuntimeError.OutOfMemory;
|
//}) catch return RuntimeError.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readOutput(self: *const Self, comptime T: type, output: []T, result: SpvWord) error{NotFound}!void {
|
pub fn readOutput(self: *const Self, comptime T: type, output: []T, result: SpvWord) ReadOutputError!void {
|
||||||
if (std.mem.indexOf(SpvWord, self.mod.output_locations.items, &.{result})) |_| {
|
if (std.mem.indexOf(SpvWord, self.mod.output_locations.items, &.{result})) |_| {
|
||||||
self.readValue(T, output, &self.results[result].variant.?.Variable.value);
|
try self.readValue(T, output, &self.results[result].variant.?.Variable.value);
|
||||||
} else {
|
} else {
|
||||||
return error.NotFound;
|
return ReadOutputError.NotFound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,13 +166,13 @@ fn reset(self: *Self) void {
|
|||||||
self.current_function = null;
|
self.current_function = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Result.Value) void {
|
fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Result.Value) ReadOutputError!void {
|
||||||
switch (value.*) {
|
switch (value.*) {
|
||||||
.Bool => |b| {
|
.Bool => |b| {
|
||||||
if (T == bool) {
|
if (T == bool) {
|
||||||
output[0] = b;
|
output[0] = b;
|
||||||
} else {
|
} else {
|
||||||
unreachable; // Wanted value may not be composed of booleans
|
return ReadOutputError.InvalidValueType;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Int => |i| {
|
.Int => |i| {
|
||||||
@@ -179,7 +185,7 @@ fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Res
|
|||||||
u16 => output[0] = i.uint16,
|
u16 => output[0] = i.uint16,
|
||||||
u32 => output[0] = i.uint32,
|
u32 => output[0] = i.uint32,
|
||||||
u64 => output[0] = i.uint64,
|
u64 => output[0] = i.uint64,
|
||||||
inline else => unreachable, // Wanted value may not be composed of ints
|
inline else => return ReadOutputError.InvalidValueType,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Float => |f| {
|
.Float => |f| {
|
||||||
@@ -187,13 +193,13 @@ fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Res
|
|||||||
f16 => output[0] = f.float16,
|
f16 => output[0] = f.float16,
|
||||||
f32 => output[0] = f.float32,
|
f32 => output[0] = f.float32,
|
||||||
f64 => output[0] = f.float64,
|
f64 => output[0] = f.float64,
|
||||||
inline else => unreachable, // Wanted value may not be composed of floats
|
inline else => return ReadOutputError.InvalidValueType,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Vector => |values| for (values, 0..) |v, i| self.readValue(T, output[i..], &v),
|
.Vector => |values| for (values, 0..) |v, i| try self.readValue(T, output[i..], &v),
|
||||||
.Matrix => |values| for (values, 0..) |v, i| self.readValue(T, output[i..], &v),
|
.Matrix => |values| for (values, 0..) |v, i| try self.readValue(T, output[i..], &v),
|
||||||
.Array => unreachable, // TODO
|
.Array => |values| for (values, 0..) |v, i| try self.readValue(T, output[i..], &v), // Doubt if this is allowed
|
||||||
.Structure => |values| for (values, 0..) |v, i| self.readValue(T, output[i..], &v),
|
.Structure => |values| for (values, 0..) |v, i| try self.readValue(T, output[i..], &v),
|
||||||
else => unreachable,
|
else => return ReadOutputError.InvalidValueType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/lib.zig
38
src/lib.zig
@@ -1,3 +1,33 @@
|
|||||||
|
//! A small footprint SPIR-V interpreter with zero dependencies to execute SPIR-V shaders on the CPU. It is designed to be used with multiple runtimes concurrently.
|
||||||
|
//!
|
||||||
|
//! ```zig
|
||||||
|
//! const std = @import("std");
|
||||||
|
//! const spv = @import("spv");
|
||||||
|
//!
|
||||||
|
//! const shader_source = @embedFile("shader.spv");
|
||||||
|
//!
|
||||||
|
//! pub fn main() !void {
|
||||||
|
//! {
|
||||||
|
//! var gpa: std.heap.DebugAllocator(.{}) = .init;
|
||||||
|
//! defer _ = gpa.deinit();
|
||||||
|
//!
|
||||||
|
//! const allocator = gpa.allocator();
|
||||||
|
//!
|
||||||
|
//! var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source)));
|
||||||
|
//! defer module.deinit(allocator);
|
||||||
|
//!
|
||||||
|
//! var rt = try spv.Runtime.init(allocator, &module);
|
||||||
|
//! defer rt.deinit(allocator);
|
||||||
|
//!
|
||||||
|
//! try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||||
|
//! var output: [4]f32 = undefined;
|
||||||
|
//! try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color"));
|
||||||
|
//! std.log.info("Output: Vec4{any}", .{output});
|
||||||
|
//! }
|
||||||
|
//! std.log.info("Successfully executed", .{});
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Image = @import("Image.zig");
|
pub const Image = @import("Image.zig");
|
||||||
@@ -6,11 +36,3 @@ pub const Runtime = @import("Runtime.zig");
|
|||||||
|
|
||||||
const opcodes = @import("opcodes.zig");
|
const opcodes = @import("opcodes.zig");
|
||||||
const spv = @import("spv.zig");
|
const spv = @import("spv.zig");
|
||||||
|
|
||||||
test {
|
|
||||||
std.testing.refAllDecls(Image);
|
|
||||||
std.testing.refAllDecls(Module);
|
|
||||||
std.testing.refAllDecls(Runtime);
|
|
||||||
std.testing.refAllDecls(opcodes);
|
|
||||||
std.testing.refAllDecls(spv);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const MathOp = enum {
|
|||||||
Div,
|
Div,
|
||||||
Mod,
|
Mod,
|
||||||
Mul,
|
Mul,
|
||||||
|
Rem,
|
||||||
Sub,
|
Sub,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -463,10 +464,8 @@ fn MathEngine(comptime T: ValueType, comptime Op: MathOp) type {
|
|||||||
if (op2 == 0) return RuntimeError.DivisionByZero;
|
if (op2 == 0) return RuntimeError.DivisionByZero;
|
||||||
break :blk if (@typeInfo(TT) == .int) @divTrunc(op1, op2) else op1 / op2;
|
break :blk if (@typeInfo(TT) == .int) @divTrunc(op1, op2) else op1 / op2;
|
||||||
},
|
},
|
||||||
.Mod => blk: {
|
.Mod => if (op2 == 0) return RuntimeError.DivisionByZero else @mod(op1, op2),
|
||||||
if (op2 == 0) return RuntimeError.DivisionByZero;
|
.Rem => if (op2 == 0) return RuntimeError.DivisionByZero else @rem(op1, op2),
|
||||||
break :blk @mod(op1, op2);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user