From 8b0b0a72aebb2691403f26fa2436bcf23e8c279f Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Sat, 17 Jan 2026 13:39:45 +0100 Subject: [PATCH] adding functions management --- example/shader.nzsl | 6 +-- example/shader.spv | Bin 692 -> 936 bytes example/shader.spv.txt | 91 ++++++++++++++++++++++++----------------- src/Result.zig | 14 ++++++- src/Runtime.zig | 10 ++++- src/opcodes.zig | 84 ++++++++++++++++++++++++------------- 6 files changed, 132 insertions(+), 73 deletions(-) diff --git a/example/shader.nzsl b/example/shader.nzsl index 7c4331d..ec65fc3 100644 --- a/example/shader.nzsl +++ b/example/shader.nzsl @@ -7,15 +7,15 @@ struct FragOut [location(0)] color: vec4[f32] } -fn computeColor() -> f32 +fn computeColor(val: f32) -> f32 { - return 1.0; + return 2.0 * val; } [entry(frag)] fn main() -> FragOut { let output: FragOut; - output.color = vec4[f32](computeColor(), computeColor(), computeColor(), computeColor()); + output.color = vec4[f32](computeColor(1.0), computeColor(2.0), computeColor(3.0), computeColor(4.0)); return output; } diff --git a/example/shader.spv b/example/shader.spv index e4a0322a33737ea35a4c5ff7619398e9d8b56fb2..afda241dd18c7ecce07cf702cbdd38dcaaeb6c24 100644 GIT binary patch literal 936 zcmZ9KNlyYn5QSR?aEDO@TyYSWgNX?j*QjGm)SG(oP7E<50%nA}G4Vfp(fGce?nvmQ zsCw^JS69^#WEzvkOqkDb&+MA?ESQWKWAHQb1rt~Su4wWmE59h~wmLn8X<4WoI2BVA zSk8p8a&zXXoxFGYz0;_9(3C7DYwO-5t(W`ZKoxTqqdoUu`-#h;?my0}A8_|=IA~wH zG+S%5ms{25UQNci8>!t$DN|8@%o174YRa^4xVr8$EiwGQFflXh#3ivg1$AZR<%z3O zncL?@!c)T3fv+iIkHl!RE`}=$&sjdb&wJo`8OOh0u;}B`~WatG4+{~x3Du| zdSNH@bEuB#lU;J)j`Yi{CE2&4CHZsRBd_fEFL$pZ%r4-6l?y&rCUw~b--5$#s8yAx z2WDQD`JQ-hWel&5;k7Zm?(l{BOx0iso&+jx5w#3j!~f(_nN(Dx!2%3!?A5RJRDCa{aY_{ zUff&uaxZVrwH2&~f8L#fvZnfDmQ=B?eV`0J)LC*d?dMfp6=p>fDYI|s8!Mv&r}`E% zo8#NkG8-CdydQPAhVYu>XZL~E6)g1I%IGE+6dTlE3;2c`&H@ca5S_`4gdfE diff --git a/example/shader.spv.txt b/example/shader.spv.txt index d11da02..59c5a0e 100644 --- a/example/shader.spv.txt +++ b/example/shader.spv.txt @@ -1,50 +1,65 @@ Version 1.0 Generator: 2560130 -Bound: 27 +Bound: 38 Schema: 0 OpCapability Capability(Shader) OpCapability Capability(Float64) OpMemoryModel AddressingModel(Logical) MemoryModel(GLSL450) - OpEntryPoint ExecutionModel(Fragment) %14 "main" %8 - OpExecutionMode %14 ExecutionMode(OriginUpperLeft) + OpEntryPoint ExecutionModel(Fragment) %18 "main" %9 + OpExecutionMode %18 ExecutionMode(OriginUpperLeft) OpSource SourceLanguage(NZSL) 4198400 OpSourceExtension "Version: 1.1" - OpName %9 "FragOut" - OpMemberName %9 0 "color" - OpName %8 "color" - OpName %13 "computeColor" - OpName %14 "main" - OpDecorate %8 Decoration(Location) 0 - OpMemberDecorate %9 0 Decoration(Offset) 0 + OpName %10 "FragOut" + OpMemberName %10 0 "color" + OpName %9 "color" + OpName %17 "computeColor" + OpName %18 "main" + OpDecorate %9 Decoration(Location) 0 + OpMemberDecorate %10 0 Decoration(Offset) 0 %1 = OpTypeFloat 32 - %2 = OpTypeFunction %1 - %3 = OpConstant %1 f32(1) - %4 = OpTypeVoid - %5 = OpTypeFunction %4 - %6 = OpTypeVector %1 4 - %7 = OpTypePointer StorageClass(Output) %6 - %9 = OpTypeStruct %6 -%10 = OpTypePointer StorageClass(Function) %9 -%11 = OpTypeInt 32 1 -%12 = OpConstant %11 i32(0) -%24 = OpTypePointer StorageClass(Function) %6 - %8 = OpVariable %7 StorageClass(Output) -%13 = OpFunction %1 FunctionControl(0) %2 -%15 = OpLabel - OpReturnValue %3 + %2 = OpTypePointer StorageClass(Function) %1 + %3 = OpTypeFunction %1 %2 + %4 = OpConstant %1 f32(2) + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeVector %1 4 + %8 = OpTypePointer StorageClass(Output) %7 +%10 = OpTypeStruct %7 +%11 = OpTypePointer StorageClass(Function) %10 +%12 = OpTypeInt 32 1 +%13 = OpConstant %12 i32(0) +%14 = OpConstant %1 f32(1) +%15 = OpConstant %1 f32(3) +%16 = OpConstant %1 f32(4) +%35 = OpTypePointer StorageClass(Function) %7 + %9 = OpVariable %8 StorageClass(Output) +%17 = OpFunction %1 FunctionControl(0) %3 +%19 = OpFunctionParameter %2 +%20 = OpLabel +%21 = OpLoad %1 %19 +%22 = OpFMul %1 %4 %21 + OpReturnValue %22 OpFunctionEnd -%14 = OpFunction %4 FunctionControl(0) %5 -%16 = OpLabel -%17 = OpVariable %10 StorageClass(Function) -%18 = OpFunctionCall %1 %13 -%19 = OpFunctionCall %1 %13 -%20 = OpFunctionCall %1 %13 -%21 = OpFunctionCall %1 %13 -%22 = OpCompositeConstruct %6 %18 %19 %20 %21 -%23 = OpAccessChain %24 %17 %12 - OpStore %23 %22 -%25 = OpLoad %9 %17 -%26 = OpCompositeExtract %6 %25 0 - OpStore %8 %26 +%18 = OpFunction %5 FunctionControl(0) %6 +%23 = OpLabel +%24 = OpVariable %11 StorageClass(Function) +%25 = OpVariable %2 StorageClass(Function) +%26 = OpVariable %2 StorageClass(Function) +%27 = OpVariable %2 StorageClass(Function) +%28 = OpVariable %2 StorageClass(Function) + OpStore %25 %14 +%29 = OpFunctionCall %1 %17 %25 + OpStore %26 %4 +%30 = OpFunctionCall %1 %17 %26 + OpStore %27 %15 +%31 = OpFunctionCall %1 %17 %27 + OpStore %28 %16 +%32 = OpFunctionCall %1 %17 %28 +%33 = OpCompositeConstruct %7 %29 %30 %31 %32 +%34 = OpAccessChain %35 %24 %13 + OpStore %34 %33 +%36 = OpLoad %10 %24 +%37 = OpCompositeExtract %7 %36 0 + OpStore %9 %37 OpReturn OpFunctionEnd diff --git a/src/Result.zig b/src/Result.zig index a14e0ef..bfcb813 100644 --- a/src/Result.zig +++ b/src/Result.zig @@ -251,13 +251,17 @@ variant: ?union(Variant) { source_location: usize, return_type: SpvWord, function_type: SpvWord, - params: []const SpvWord, + params: []SpvWord, }, AccessChain: struct { target: SpvWord, value: Value, }, - FunctionParameter: struct {}, + FunctionParameter: struct { + type_word: SpvWord, + type: Type, + value_ptr: ?*Value, + }, Label: struct { source_location: usize, }, @@ -293,6 +297,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { .Constant => |*c| c.value.deinit(allocator), .Variable => |*v| v.value.deinit(allocator), //.AccessChain => |*a| a.value.deinit(allocator), + .Function => |f| allocator.free(f.params), else => {}, } } @@ -303,6 +308,8 @@ pub fn getValueTypeWord(self: *Self) RuntimeError!SpvWord { return switch (self.variant orelse return RuntimeError.InvalidSpirV) { .Variable => |v| v.type_word, .Constant => |c| c.type_word, + .AccessChain => |*a| a.target, + .FunctionParameter => |p| p.type_word, else => RuntimeError.InvalidSpirV, }; } @@ -311,6 +318,7 @@ pub fn getValueType(self: *Self) RuntimeError!Type { return switch (self.variant orelse return RuntimeError.InvalidSpirV) { .Variable => |v| v.type, .Constant => |c| c.type, + .FunctionParameter => |p| p.type, else => RuntimeError.InvalidSpirV, }; } @@ -319,6 +327,8 @@ pub fn getValue(self: *Self) RuntimeError!*Value { return switch (self.variant orelse return RuntimeError.InvalidSpirV) { .Variable => |*v| &v.value, .Constant => |*c| &c.value, + .AccessChain => |*a| &a.value, + .FunctionParameter => |*p| p.value_ptr orelse return RuntimeError.InvalidSpirV, else => RuntimeError.InvalidSpirV, }; } diff --git a/src/Runtime.zig b/src/Runtime.zig index 0a2b028..8e678ee 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -27,6 +27,7 @@ pub const RuntimeError = error{ pub const Function = struct { source_location: usize, result: *Result, + ret: *Result, }; mod: *Module, @@ -35,6 +36,7 @@ it: WordIterator, /// Local deep copy of module's results to be able to run multiple runtimes concurrently results: []Result, +current_parameter_index: SpvWord, current_function: ?*Result, function_stack: std.ArrayList(Function), @@ -49,6 +51,7 @@ pub fn init(allocator: std.mem.Allocator, module: *Module) RuntimeError!Self { } break :blk results; }, + .current_parameter_index = 0, .current_function = null, .function_stack = .empty, }; @@ -103,6 +106,11 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind switch (variant) { .Function => |f| { if (!self.it.jumpToSourceLocation(f.source_location)) return RuntimeError.InvalidEntryPoint; + self.function_stack.append(allocator, .{ + .source_location = f.source_location, + .result = entry_point_result, + .ret = &self.results[f.return_type], + }) catch return RuntimeError.OutOfMemory; }, else => return RuntimeError.InvalidEntryPoint, } @@ -127,7 +135,7 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind self.it = it_tmp; } else { self.it.did_jump = false; - _ = it_tmp.skip(); + //_ = self.it.skip(); } } diff --git a/src/opcodes.zig b/src/opcodes.zig index fa02366..b097a6a 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -75,6 +75,7 @@ pub const SetupDispatcher = block: { .Function = opFunction, .FunctionCall = autoSetupConstant, .FunctionEnd = opFunctionEnd, + .FunctionParameter = opFunctionParameter, .IAdd = autoSetupConstant, .IEqual = autoSetupConstant, .IMul = autoSetupConstant, @@ -697,10 +698,55 @@ fn opFunction(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeErr rt.current_function = &rt.mod.results[id]; } +fn opFunctionCall(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + _ = rt.it.skip(); + const ret = &rt.results[try rt.it.next()]; + const func = &rt.results[try rt.it.next()]; + + for ((func.variant orelse return RuntimeError.InvalidSpirV).Function.params) |param| { + const arg = &rt.results[try rt.it.next()]; + (rt.results[param].variant orelse return RuntimeError.InvalidSpirV).FunctionParameter.value_ptr = try arg.getValue(); + } + rt.function_stack.items[rt.function_stack.items.len - 1].source_location = rt.it.emitSourceLocation(); + const source_location = (func.variant orelse return RuntimeError.InvalidSpirV).Function.source_location; + rt.function_stack.append(allocator, .{ + .source_location = source_location, + .result = func, + .ret = ret, + }) catch return RuntimeError.OutOfMemory; + if (!rt.it.jumpToSourceLocation(source_location)) return RuntimeError.InvalidSpirV; + rt.current_parameter_index = 0; +} + fn opFunctionEnd(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { rt.current_function = null; } +fn opFunctionParameter(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const var_type = try rt.it.next(); + const id = try rt.it.next(); + + const target = &rt.mod.results[id]; + + const resolved = rt.mod.results[var_type].resolveType(rt.mod.results); + const member_count = resolved.getMemberCounts(); + if (member_count == 0) { + return RuntimeError.InvalidSpirV; + } + target.variant = .{ + .FunctionParameter = .{ + .type_word = var_type, + .type = switch (resolved.variant orelse return RuntimeError.InvalidSpirV) { + .Type => |t| @as(Result.Type, t), + else => return RuntimeError.InvalidSpirV, + }, + .value_ptr = null, + }, + }; + ((rt.current_function orelse return RuntimeError.InvalidSpirV).variant orelse return RuntimeError.InvalidSpirV).Function.params[rt.current_parameter_index] = id; + rt.current_parameter_index += 1; +} + fn opLabel(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { const id = try rt.it.next(); rt.mod.results[id].variant = .{ @@ -714,20 +760,7 @@ fn opLoad(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { _ = rt.it.skip(); const id = try rt.it.next(); const ptr_id = try rt.it.next(); - copyValue( - switch (rt.results[id].variant orelse return RuntimeError.InvalidSpirV) { - .Variable => |*v| &v.value, - .Constant => |*c| &c.value, - .AccessChain => |*a| &a.value, - else => return RuntimeError.InvalidSpirV, - }, - switch (rt.results[ptr_id].variant orelse return RuntimeError.InvalidSpirV) { - .Variable => |v| &v.value, - .Constant => |c| &c.value, - .AccessChain => |a| &a.value, - else => return RuntimeError.InvalidSpirV, - }, - ); + copyValue(try rt.results[id].getValue(), try rt.results[ptr_id].getValue()); } fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { @@ -775,7 +808,6 @@ fn opName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runti } fn opReturn(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { - rt.last_return_id = null; _ = rt.function_stack.pop(); if (rt.function_stack.getLastOrNull()) |function| { _ = rt.it.jumpToSourceLocation(function.source_location); @@ -787,6 +819,13 @@ fn opReturn(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { } fn opReturnValue(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + if (rt.function_stack.getLastOrNull()) |function| { + var ret_res = rt.results[try rt.it.next()]; + copyValue(try function.ret.getValue(), try ret_res.getValue()); + } else { + return RuntimeError.InvalidSpirV; // No current function ??? + } + _ = rt.function_stack.pop(); if (rt.function_stack.getLastOrNull()) |function| { _ = rt.it.jumpToSourceLocation(function.source_location); @@ -824,20 +863,7 @@ fn opSourceExtension(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run fn opStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { const ptr_id = try rt.it.next(); const val_id = try rt.it.next(); - copyValue( - switch (rt.results[ptr_id].variant orelse return RuntimeError.InvalidSpirV) { - .Variable => |*v| &v.value, - .Constant => |*c| &c.value, - .AccessChain => |*a| &a.value, - else => return RuntimeError.InvalidSpirV, - }, - switch (rt.results[val_id].variant orelse return RuntimeError.InvalidSpirV) { - .Variable => |v| &v.value, - .Constant => |c| &c.value, - .AccessChain => |a| &a.value, - else => return RuntimeError.InvalidSpirV, - }, - ); + copyValue(try rt.results[ptr_id].getValue(), try rt.results[val_id].getValue()); } fn opTypeBool(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {